1 首先在Face++注册一个自己的账号
https://console.faceplusplus.com.cn/dashboard

人脸

人脸(Face)在人脸识别技术中特指图像中发现的人脸,当对一张图片进行人脸检测时,会将检测到的人脸记录下来,包括人脸在图片中的位置,用一个系统标识face_token来表示。注意:对同一张图片进行多次人脸检测,对同一个人脸会得到不同的face_token。

人脸集合

人脸集合(FaceSet)是用来存储检测到人脸的存储对象。一个FaceSet内face_token是不重复的

人脸比对/人脸搜索

计算机检测到图片中一个人脸之后,通过人脸判断人身份的过程被称为人脸比对/人脸搜索。
人脸比对指采集新的人脸,与一个已知身份用户的人脸进行比对,判断新的人脸是否属于该已知身份用户。人脸比对需要调用Compare API。
人脸搜索是指采集用户新的人脸,在多个已知身份用户的人脸集合中进行搜索,找出新的人脸属于哪一个已知身份用户。
人脸搜索需要调用Search API。

创建API Key

要调用 API,需要先创建一个 API Key(API 密钥),它是使用 API 和 SDK 的凭证。注册验证成功后,您可以在欢迎页面点击“创建我的第一个应用”,一个免费API Key会被自动生成,您可以直接使用。(注:免费API key 可依据免费规则调用API,如果您希望使用付费版服务,请按下面步骤创建正式API Key。)

java 实现动态捕捉人脸 java怎么实现人脸识别_API


完成 API Key 创建之后,您可以在控制台看到自己的账户余额和API调用量的统计。

java 实现动态捕捉人脸 java怎么实现人脸识别_java 实现动态捕捉人脸_02


postman调用API接口

java 实现动态捕捉人脸 java怎么实现人脸识别_计算机视觉_03

人脸检测

人脸检测的API文档

执行下述步骤以后,看返回的结果

java 实现动态捕捉人脸 java怎么实现人脸识别_java_04


通过对比API参数可以看到 响应字段的意思

人脸对比

API文档

java 实现动态捕捉人脸 java怎么实现人脸识别_java_05


传两张不一样的照片,再看看置信度

java 实现动态捕捉人脸 java怎么实现人脸识别_人工智能_06

创建faceSet

API文档 产生了一个faceset集合

java 实现动态捕捉人脸 java怎么实现人脸识别_计算机视觉_07

添加face到faceset

api文档 成功添加到人脸集合里

java 实现动态捕捉人脸 java怎么实现人脸识别_java_08

根据outer_id得到faceset

api文档

可以看到有一张人脸

java 实现动态捕捉人脸 java怎么实现人脸识别_人工智能_09


继续往faceset添加一张图片可以检测到数量已经变为2

java 实现动态捕捉人脸 java怎么实现人脸识别_java_10

删除人脸

api文档

java 实现动态捕捉人脸 java怎么实现人脸识别_java_11


可以看到删除后数量已经减少了

java 实现动态捕捉人脸 java怎么实现人脸识别_java_12

人脸识别实现刷脸登录

录入人脸信息,通过人脸检测接口得到face_token,存入outer_id=travel_faceset的faceset中当用户在登录的时候,得到用户当前登录图片,然后用人脸比对接口用当前登录图片和之前在faceset中保存的人脸信息进行比对。
比对后的返回信息有我们设置的阈值(thresholds),可以根据自身的需求选择其中一个阈值,当相似度(confidence)高于这个阈值时就可以认为是同一个人,即可以登录成功,反之,不能登录成功。

本项目是基于springboot项目的 。所以接下来的配置是在我的一个已有的springboot项目上进行人脸登录的

需要在配置文件中添加

face:
  config:
    api-key: JtGRv7lXpLK3wkip5KKIdDaOOKe1J-Cx
    api-secret: 1zZ-eiAQiw7x-yG0gQ6ta0tmIB9GqLDg
    outer-id: faceset1

启动类中配置restTemplate

@SpringBootApplication
public class TravelApplication {
@Bean
public RestTemplate restTemplate() {
  return new RestTemplate();
}
public static void main(String[] args) {
  SpringApplication.run(TravelApplication.class, args);
}
}

下面就是DAO层的业务,和上边的postman里边的请求是一样的

DAO层

package com.xxq.mapper;

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.HashMap;
import java.util.List;
import java.util.Map;

@ConfigurationProperties("face.config")
@Component
@Getter
@Setter
public class FaceDao {

    @Autowired
    private RestTemplate restTemplate;

    private String apiKey;
    private String apiSecret;
    private String outerId;

/**
 * 人脸检测
 * @param filePath
 * @return
 * /
 * */

    public DetectResponseEntity detect(String filePath) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 多部件表单体
        MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
       // ----------------- 表单 part  普通域
        multipartBodyBuilder.part("api_key", apiKey);
        multipartBodyBuilder.part("api_secret", apiSecret);
         // ----------------- 文件域 part
        // 从磁盘读取文件
        multipartBodyBuilder.part("image_file", new FileSystemResource(Paths.get(filePath)), MediaType.IMAGE_PNG);
       // build完整的消息体
        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();
    }
    /**
     * 创建用于登录的人脸faseSet
     */
    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);
    }
    /**
     * 得到登录人脸FaceSet详细信息
     */
    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://api-cn.faceplusplus.com/facepp/v3/faceset/getdetail", request,
                        FaceSetResponseEntity.class);
        return responseEntity.getBody();
    }
    /**
     * 添加faceToken到FaceSet
     * 人脸标识 faceTokens 组成的字符串,可以是一个或者多个,用逗号分隔。最多不超过5个
     face_token
     * @param faceTokens
     */
    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://api-cn.faceplusplus.com/facepp/v3/faceset/addface", request, String.class);
    }
    /**
     * 人脸比对
     * @param faceToken1
     * @param faceToken2
     * @return
     */
    public boolean compareFace(String faceToken1, String faceToken2) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 多部件表单体
        MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
        // ----------------- 表单 part
        multipartBodyBuilder.part("api_key", apiKey);
        multipartBodyBuilder.part("api_secret", apiSecret);
        multipartBodyBuilder.part("face_token1", faceToken1);
        multipartBodyBuilder.part("face_token2", faceToken2);
        // ----------------- 文件 part
         // 从磁盘读取文件
        //multipartBodyBuilder.part("image_file", new FileSystemResource(Paths.get(filePath)), MediaType.IMAGE_PNG);
        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;
    }
    /**
     * FaceSet返回实体类
     */
    @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;
    }
}

测试类

package com.xxq.mapper;

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;

@SpringBootTest
@RunWith(SpringRunner.class)

public class FaceDaoTest {

    @Autowired
    private  FaceDao faceDao;
    @Test
   public void detect() {
       FaceDao.DetectResponseEntity entity=faceDao.detect("C:\\Users\\ly\\Desktop\\photo\\向.jpg");
       System.out.println(entity);
    }
    @Test
    public void getFaceSetDetail() {
        FaceDao.FaceSetResponseEntity e = faceDao.getFaceSetDetail();
        System.out.println(e);
    }
    // 这个方法只适用于没有outerID的faceset,有的话就创建不了,报错了。
    @Test
    public void createFaceSet() {
        faceDao.faceSetCreate();
    }
    @Test
    public void addFaceToFaceSet() {
        faceDao.addFaceToFaceSet("af1f0bd2054dfe2aa1ce06c3be7116fa");
    }
    @Test
    public void campareFace() {
        boolean b = faceDao.compareFace("04aeead26262cea830b63ec0c0a92507",
                "af1f0bd2054dfe2aa1ce06c3be7116fa");
        System.out.println(b);
    }

}

**

报错 java.lang.NoClassDefFoundError: org/reactivestreams/Publisher
添加下面的构件

**

<dependency>
  <groupId>org.reactivestreams</groupId>
  <artifactId>reactive-streams</artifactId>
  <version>1.0.3</version>
</dependency>

Service层

接口层

public interface FaceService {
    /*
    * 录入人脸
    * */
    public  void  addFace(String filePath);
    /*
    * 登录比对人脸
    * */
    public boolean loginByFace(String filePath);
}

实现层

package com.xxq.service.impl;

import com.xxq.service.FaceService;
import com.xxq.mapper.FaceDao;
import com.xxq.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) { //faceset不存在
                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) { //faceset不存在
                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;
        }
    }

Controller层

package com.xxq.controller;
import com.xxq.service.FaceService;
import com.xxq.util.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;
    /**
     * 调到录入人脸页面
     * @return
     */
    @RequestMapping("/toinput")
    public String toInput() {
        return "/face/input";
    }
    @RequestMapping("/tologin")
    public String toLogin() {
        return "/face/login";
    }
    /**
     * 录入人脸
     * @param imgData
     * @param request
     * @return
     * @throws IOException
     */
    @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\"}");
    }
    /**
     * 人脸识别登录
     * @param imgData
     * @param request
     * @return
     * @throws IOException
     */
    @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}");
        }
    }
}

工具类,进行BASE64转码的

package com.xxq.util;
import sun.misc.BASE64Decoder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class ImageUtils {

    public static boolean generateImage(String imgStr, String filePath, String fileName) {
        try {
            if (imgStr == null) {
                return false;
            }
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] b = decoder.decodeBuffer(imgStr);
            File file = new File(filePath);
            if (!file.exists()) {
                file.mkdirs();
            }
            OutputStream out = new FileOutputStream(filePath+fileName);
            out.write(b);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

}

前端页面:

java 实现动态捕捉人脸 java怎么实现人脸识别_人工智能_13

input.jtml

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta charset="utf-8"/>
    <title>用户管理</title>

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>

    <!-- bootstrap & fontawesome -->
    <link rel="stylesheet" href="/assets/css/bootstrap.min.css"/>
    <link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css"/>

    <!-- page specific plugin styles -->

    <!-- text fonts -->
    <link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css"/>

    <!-- ace styles -->
    <link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style"/>

    <!--[if lte IE 9]>
    <link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet"/>
    <![endif]-->
    <link rel="stylesheet" href="/assets/css/ace-skins.min.css"/>
    <link rel="stylesheet" href="/assets/css/ace-rtl.min.css"/>

    <!--[if lte IE 9]>
    <link rel="stylesheet" href="/assets/css/ace-ie.min.css"/>
    <![endif]-->

    <!-- inline styles related to this page -->

    <!-- ace settings handler -->
    <script src="/assets/js/ace-extra.min.js"></script>

    <!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries -->

    <!--[if lte IE 8]>
    <script src="/assets/js/html5shiv.min.js"></script>
    <script src="/assets/js/respond.min.js"></script>
    <![endif]-->

    <!--[if !IE]> -->
    <script src="/assets/js/jquery-2.1.4.min.js"></script>

    <!-- <![endif]-->

    <!--[if IE]>
    <script src="/assets/js/jquery-1.11.3.min.js"></script>
    <![endif]-->
    <script src="/assets/js/bootstrap.min.js"></script>

    <!-- page specific plugin scripts -->
    <script src="/assets/js/jquery.dataTables.min.js"></script>
    <script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script>
    <script src="/assets/js/dataTables.buttons.min.js"></script>
    <script src="/assets/js/buttons.flash.min.js"></script>
    <script src="/assets/js/buttons.html5.min.js"></script>
    <script src="/assets/js/buttons.print.min.js"></script>
    <script src="/assets/js/buttons.colVis.min.js"></script>
    <script src="/assets/js/dataTables.select.min.js"></script>

    <!-- ace scripts -->
    <script src="/assets/js/ace-elements.min.js"></script>
    <script src="/assets/js/ace.min.js"></script>

</head>

<body class="no-skin">

<div th:replace="header :: navbar"></div>

<div class="main-container ace-save-state" id="main-container">
    <script type="text/javascript">
        try {
            ace.settings.loadState('main-container')
        } catch (e) {
        }
    </script>

    <div th:replace="left :: sidebar"></div>

    <div class="main-content">
        <div class="main-content-inner">
            <div class="breadcrumbs ace-save-state" id="breadcrumbs">
                <ul class="breadcrumb">
                    <li>
                        <i class="ace-icon fa fa-home home-icon"></i>
                        <a href="#">首页</a>
                    </li>
                    <li>
                        <a href="#">用户</a>
                    </li>
                    <li class="active">用户管理</li>
                </ul><!-- /.breadcrumb -->

            </div>

            <div class="page-content">

                <div style="padding: 10px;">
                    <tr>
                        <td colspan="2">
                            <button class="btn btn-sm btn-default" onclick="openMedia()">开启摄像头</button>
                            <button class="btn btn-sm btn-default" onclick="closeMedia()">关闭摄像头</button>
                            <button class="btn btn-sm btn-default" onclick="takePhoto()">录入人脸/button>
                        </td>
                    </tr>
                    <table>
                        <tr>
                            <td>
                                <video id="video" width="500px" height="500px" autoplay="autoplay"></video>
                                <canvas id="canvas" width="500px" height="500px" style="display: none"></canvas>
                            </td>
                            <td>
                                <img id="imgTag" src="" alt="..." width="500px" height="500px"><br>
                            </td>
                        </tr>
                    </table>

                    <script>
                        let mediaStreamTrack=null; // 视频对象(全局)
                        let video ;
                        function openMedia() {
                            let constraints = {
                                video: { width: 500, height: 500 },
                                audio: false
                            };
                            //获得video摄像头
                            video = document.getElementById('video');
                            let promise = navigator.mediaDevices.getUserMedia(constraints);
                            promise.then((mediaStream) => {
                                // mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[1];
                                mediaStreamTrack=mediaStream.getVideoTracks()
                                video.srcObject = mediaStream;
                                video.play();
                            });
                        }

                        // 拍照
                        function takePhoto() {
                            //获得Canvas对象
                            let video = document.getElementById('video');
                            let canvas = document.getElementById('canvas');
                            let ctx = canvas.getContext('2d');
                            ctx.drawImage(video, 0, 0, 500, 500);


                            // toDataURL  ---  可传入'image/png'---默认, 'image/jpeg'
                            let img = document.getElementById('canvas').toDataURL();
                            // 这里的img就是得到的图片
                            console.log('img-----', img);
                            document.getElementById('imgTag').src=img;
                            //上传
                            $.ajax({
                                url:"/admin/face/upload",
                                type:"POST",
                                data:{"imgData":img},
                                success:function(data){
                                    alert("录入成功")
                                }
                                ,error:function(){
                                    alert("录入失败")
                                }
                            });

                        }

                        // 关闭摄像头
                        function closeMedia() {
                            let stream = document.getElementById('video').srcObject;
                            let tracks = stream.getTracks();

                            tracks.forEach(function(track) {
                                track.stop();
                            });

                            document.getElementById('video').srcObject = null;
                        }
                    </script>
                </div>

            </div><!-- /.page-content -->
        </div>
    </div><!-- /.main-content -->

</div><!-- /.main-container -->

</body>
</html>

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta charset="utf-8"/>
    <title>用户管理</title>

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>

    <!-- bootstrap & fontawesome -->
    <link rel="stylesheet" href="/assets/css/bootstrap.min.css"/>
    <link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css"/>

    <!-- page specific plugin styles -->

    <!-- text fonts -->
    <link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css"/>

    <!-- ace styles -->
    <link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style"/>

    <!--[if lte IE 9]>
    <link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet"/>
    <![endif]-->
    <link rel="stylesheet" href="/assets/css/ace-skins.min.css"/>
    <link rel="stylesheet" href="/assets/css/ace-rtl.min.css"/>

    <!--[if lte IE 9]>
    <link rel="stylesheet" href="/assets/css/ace-ie.min.css"/>
    <![endif]-->

    <!-- inline styles related to this page -->

    <!-- ace settings handler -->
    <script src="/assets/js/ace-extra.min.js"></script>

    <!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries -->

    <!--[if lte IE 8]>
    <script src="/assets/js/html5shiv.min.js"></script>
    <script src="/assets/js/respond.min.js"></script>
    <![endif]-->

    <!--[if !IE]> -->
    <script src="/assets/js/jquery-2.1.4.min.js"></script>

    <!-- <![endif]-->

    <!--[if IE]>
    <script src="/assets/js/jquery-1.11.3.min.js"></script>
    <![endif]-->
    <script src="/assets/js/bootstrap.min.js"></script>

    <!-- page specific plugin scripts -->
    <script src="/assets/js/jquery.dataTables.min.js"></script>
    <script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script>
    <script src="/assets/js/dataTables.buttons.min.js"></script>
    <script src="/assets/js/buttons.flash.min.js"></script>
    <script src="/assets/js/buttons.html5.min.js"></script>
    <script src="/assets/js/buttons.print.min.js"></script>
    <script src="/assets/js/buttons.colVis.min.js"></script>
    <script src="/assets/js/dataTables.select.min.js"></script>

    <!-- ace scripts -->
    <script src="/assets/js/ace-elements.min.js"></script>
    <script src="/assets/js/ace.min.js"></script>

</head>

<body class="no-skin">

<div class="main-container ace-save-state" id="main-container">

    <div class="main-content">
        <div class="main-content-inner">

            <div class="page-content">

                <div style="padding: 10px;">
                    <tr>
                        <td colspan="2">
                            <button class="btn btn-sm btn-default" onclick="openMedia()">开启摄像头</button>
                            <button class="btn btn-sm btn-default" onclick="closeMedia()">关闭摄像头</button>
                            <button class="btn btn-sm btn-default" onclick="takePhoto()">登录</button>
                        </td>
                    </tr>
                    <table>
                        <tr>
                            <td>
                                <video id="video" width="500px" height="500px" autoplay="autoplay"></video>
                                <canvas id="canvas" width="500px" height="500px" style="display: none"></canvas>
                            </td>
                            <td>
                                <img id="imgTag" src="" alt="..." width="500px" height="500px"><br>
                            </td>
                        </tr>
                    </table>

                    <script>
                        let mediaStreamTrack=null; // 视频对象(全局)
                        let video ;
                        function openMedia() {
                            let constraints = {
                                video: { width: 500, height: 500 },
                                audio: false
                            };
                            //获得video摄像头
                            video = document.getElementById('video');
                            let promise = navigator.mediaDevices.getUserMedia(constraints);
                            promise.then((mediaStream) => {
                                // mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[1];
                                mediaStreamTrack=mediaStream.getVideoTracks()
                                video.srcObject = mediaStream;
                                video.play();
                            });
                        }

                        // 拍照
                        function takePhoto() {
                            //获得Canvas对象
                            let video = document.getElementById('video');
                            let canvas = document.getElementById('canvas');
                            let ctx = canvas.getContext('2d');
                            ctx.drawImage(video, 0, 0, 500, 500);


                            // toDataURL  ---  可传入'image/png'---默认, 'image/jpeg'
                            let img = document.getElementById('canvas').toDataURL();
                            // 这里的img就是得到的图片
                            console.log('img-----', img);
                            document.getElementById('imgTag').src=img;
                            //上传
                            $.ajax({
                                url:"/admin/face/login",
                                type:"POST",
                                data:{"imgData":img},
                                dataType: "json",
                                success:function(data){
                                    var b = data.success;
                                    alert(b);
                                    if (b) {
                                        alert("登录成功");
                                    } else {
                                        alert("登录失败");
                                    }

                                }
                                ,error:function(){
                                    alert("登录失败")
                                }
                            });

                        }

                        // 关闭摄像头
                        function closeMedia() {
                            let stream = document.getElementById('video').srcObject;
                            let tracks = stream.getTracks();

                            tracks.forEach(function(track) {
                                track.stop();
                            });

                            document.getElementById('video').srcObject = null;
                        }
                    </script>
                </div>

            </div><!-- /.page-content -->
        </div>
    </div><!-- /.main-content -->

</div><!-- /.main-container -->

</body>
</html>