是一个项目的一个功能之一,调试了两小时,终于能够
javascript设置开始计和暂停计时 监控人脸 记录时间了
效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
离开页面之后回到页面会从0计时(不是关闭页面,而是页面失去焦点)
在这里插入图片描述
离开摄像头时会弹出提示。
在这里插入图片描述
离开摄像头反馈给后端的时间。

全部代码:


<template>
  <div class="camera_outer">
   
    <video src="../assets/shu.mp4" style="width: 600px;height: 600px;margin-left: 150px" controls="controls"></video>
<hr>
    <el-button type="warning" @click.native="gettime()" style="margin-left: 300px"><i class="el-icon-video-camera-solid"></i> 开始听课</el-button>

    <el-button type="primary" @click="computetime()"><i class="el-icon-price-tag"></i> 结束听课</el-button>
    <el-button type="success" @click="out()"><i class="el-icon-loading
"></i> 暂时离开</el-button>


    <div style="width: 130px;background: #00eeee;margin-left: 350px" v-if="this.set==true">

      <i class="el-icon-alarm-clock">
      </i>您上课的时间:<div ref="startTimer"></div></div>

    <video
        id="videoCamera"
        :width="videoWidth"
        :height="videoHeight"
        autoplay
    ></video>
    <br>
    <canvas

        id="canvasCamera"
        :width="videoWidth"
        :height="videoHeight"
    ></canvas>
  </div>

</template>
<script>

export default {
  beforeRouteLeave (to, from, next) {
    // 这里需要elementui的支持,如果使用其他界面组件自行替换即可
    this.$confirm('正在离开本页面,本页面时间将从零开始计时', '警告', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }).then(() => {
      // 正常跳转
      next()
    }).catch(() => {
      // 如果取消跳转地址栏会变化,这时保持地址栏不变
      window.history.go(1)
    })
  },
  data() {
    return {
      activeIndex2: '1',
      timer: "",
      content: "",
      hour: 0,
      minutes: 0,
      seconds: 0,
      videoWidth: 150,
      videoHeight: 150,
      imgSrc: "",
      thisCancas: null,
      thisContext: null,
      thisVideo: null,
      total:0,
      nowtime:[],
      set:true,
      yes:true,
      begintime:''
    };
  },
  created() {

    document.addEventListener('visibilitychange', this.startStopVideo)
  },
  mounted() {

      this.getCompetence();

      //this.gettime()

      var _this = this;
      this.thisCancas = document.getElementById("canvasCamera");
      this.thisContext = this.thisCancas.getContext("2d");
      this.thisVideo = document.getElementById("videoCamera");
      window.setInterval(
          this.setImage
          , 2000);

  },
  methods: {

       startStopVideo() {
  if (document.visibilityState === 'hidden') {

    if(this.yes==true){
      var stop1 = clearInterval(this.startTimer);
      this.yes = false;
    }
    else if(this.yes==false) {
      var stop2 = clearInterval(this.startTimer);
      this.yes = true

    }
window.location.reload()
  } else if (document.visibilityState === 'visible') {
    this.$message({
      message: '您刚刚离开了观看页面,将从零开始计时!',
      type: 'warning'
    });

  
    this.getCompetence();
    var _this = this;
    this.thisCancas = document.getElementById("canvasCamera");
    this.thisContext = this.thisCancas.getContext("2d");
    this.thisVideo = document.getElementById("videoCamera");
    window.setInterval(
        this.setImage
        , 2000);
  }
},
    out(){
      var stop1 = clearInterval(this.timer);
    },
    startTimer () {

        this.seconds += 1;
        if (this.seconds >= 60) {
          this.seconds = 0;
          this.minutes = this.minutes + 1;
        }

        if (this.minutes >= 60) {
          this.minutes = 0;
          this.hour = this.hour + 1;
        }
        this.total = this.minutes + this.hour * 60
        this.$refs.startTimer.innerHTML = (this.minutes < 10 ? '0' + this.minutes : this.minutes) + ':' + (this.seconds < 10 ? '0' + this.seconds : this.seconds) + '  total:' + this.total;

    },
    computetime(){

         var that = this
    this.set = false;
      this.$axios.post(
          "/gettime",
          {

            timenot: this.nowtime,
            total: this.total + '分',
            begintime: this.begintime

          }
      ).then(resp => {
        if (resp && resp.status === 200) {
          //
          that.$message({
                type: 'success',
                message: '您有一条新的学习记录生成!'}
            )
        }
        })
    },
    gettime(){
         this.begintime=new Date().toLocaleTimeString();
      this.timer = setInterval(this.startTimer, 1000);
    },
    // 调用权限(打开摄像头功能)
    getCompetence() {
      var _this = this;
      this.thisCancas = document.getElementById("canvasCamera");
      this.thisContext = this.thisCancas.getContext("2d");
      this.thisVideo = document.getElementById("videoCamera");
      // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
      if (navigator.mediaDevices === undefined) {
        navigator.mediaDevices = {};
      }
      // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
      // 使用getUserMedia,因为它会覆盖现有的属性。
      // 这里,如果缺少getUserMedia属性,就添加它。
      if (navigator.mediaDevices.getUserMedia === undefined) {
        navigator.mediaDevices.getUserMedia = function (constraints) {
          // 首先获取现存的getUserMedia(如果存在)
          var getUserMedia =
              navigator.webkitGetUserMedia ||
              navigator.mozGetUserMedia ||
              navigator.getUserMedia;
          // 有些浏览器不支持,会返回错误信息
          // 保持接口一致
          if (!getUserMedia) {
            return Promise.reject(
                new Error("getUserMedia is not implemented in this browser")
            );
          }
          // 否则,使用Promise将调用包装到旧的navigator.getUserMedia
          return new Promise(function (resolve, reject) {
            getUserMedia.call(navigator, constraints, resolve, reject);
          });
        };
      }
      var constraints = {
        audio: false,
        video: {
          width: this.videoWidth,
          height: this.videoHeight,
          transform: "scaleX(-1)",
        },
      };
      navigator.mediaDevices
          .getUserMedia(constraints)
          .then(function (stream) {
            // 旧的浏览器可能没有srcObject
            if ("srcObject" in _this.thisVideo) {
              _this.thisVideo.srcObject = stream;
            } else {
              // 避免在新的浏览器中使用它,因为它正在被弃用。
              _this.thisVideo.src = window.URL.createObjectURL(stream);
            }
            _this.thisVideo.onloadedmetadata = function (e) {
              _this.thisVideo.play();
            };
          })
          .catch((err) => {
            console.log(err);
          });
    },
    //  绘制图片(拍照功能)
    setImage() {
      var date = new Date();

      date .getYear(); //获取当前年份(2位)

      date .getFullYear(); //获取完整的年份(4位)

      date .getMonth(); //获取当前月份(0-11,0代表1月)

      date .getDate(); //获取当前日(1-31)

      date.getDay(); //获取当前星期X(0-6,0代表星期天)

      date.getTime(); //获取当前时间(从1970.1.1开始的毫秒数)

      date.getHours(); //获取当前小时数(0-23)

      date.getMinutes(); //获取当前分钟数(0-59)

      date.getSeconds(); //获取当前秒数(0-59)
      var _this = this;
   


      _this.thisContext.drawImage(
          _this.thisVideo,
          0,
          0,
          _this.videoWidth,
          _this.videoHeight
      );
      // 获取图片base64链接
      var image = this.thisCancas.toDataURL("image/png").split('base64,')[1];
      _this.imgSrc = image;
      console.log(_this.imgSrc)
      this.$axios.post('http://localhost:5000/getpic', {data: _this.imgSrc}).then(resp => {
        if (resp && resp.status === 200) {
          //状态码为200为请求成功
          //手动构造base64路径:前缀+返回码
          this.base64code = 'data:image/png;base64,' + resp.data.base64code
          if(resp.data == 'b')

          {  this.$message.error( date.getHours()+':'+date.getMinutes()+':'+date.getSeconds()+"请您勿离开摄像头视野!")
            let thistime =date.getHours()+':'+date.getMinutes()+':'+date.getSeconds()
            this.nowtime.push(thistime)
          }

          _this.thisContext.clearRect(0,0, _this.videoWidth,_this.videoHeight);
          // location.reload();
          //console.log(this.base64code)
        }
      })
    }
  }, beforeDestroy() {
    document.removeEventListener('visibilitychange', this.startStopVideo)

  }

};
</script>

其中包含了图片解码编码,打开摄像头,获取后端结果。
服务器有两个,一个是python的pytorch深度学习处理图片 在flask框架下,一个是java的springboot来获得离开摄像头的时间。
java部分:
实体类:

package com.naughty.userlogin02.bean;

import lombok.Data;

@Data
public class Nowtime {
    int id;
     String timenot;
   String total;
  String  nowtime;
  String begintime;

}

跨域请求:

@RestController
public class Timecontroller {
    @Autowired
    TimeDao timeDao;

    static int id = 0;
    @CrossOrigin
    @PostMapping("/gettime")
    public String getteacherList(@RequestBody String time){

        id++;
        System.out.println(time);
    //    System.out.println(nowtime.getId());
     Map<String, Object> jsonMap = JSON.parseObject(time);
        System.out.println(jsonMap.get("total"));
        LocalDate date = LocalDate.now();
        System.out.println(date);
        Nowtime nowtime = new Nowtime();
        nowtime.setNowtime(date.toString());
        String ns=jsonMap.get("timenot").toString();
        String totaltime=jsonMap.get("total").toString();
        String begintime = jsonMap.get("begintime").toString();
        nowtime.setTimenot(ns);
        nowtime.setTotal(totaltime);
        nowtime.setId(id);
        nowtime.setBegintime(begintime);
        timeDao.addtime(nowtime);
        return "ok";


        //return timenot;
    }
    @GetMapping("/gettime")
    public String getalltime(){
        System.out.println("time!");
     List<Nowtime> nowtimes = timeDao.getall();
        HashMap<String, Object> res = new HashMap<>();
        res.put("data",nowtimes);
        String users_json = JSON.toJSONString(res);
        return users_json;
    }

}

xml语句:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.naughty.userlogin02.dao.TimeDao">

    <insert id="addtime" parameterType="com.naughty.userlogin02.bean.Nowtime">
        insert into data1.gettime(timenot,total,nowtime,begintime) values (#{timenot},#{total},#{nowtime},#{begintime});
    </insert>
    <select id="getall" resultType="com.naughty.userlogin02.bean.Nowtime">
        SELECT * FROM gettime
        <if test="nowtime !=null">
            WHERE nowtime like #{nowtime}
        </if>

    </select>
</mapper>

python的主程序:

import base64
from predict import class_names
import torch
from torchvision import datasets, models, transforms
import cv2
import numpy as np
import requests
from flask import Flask,make_response, jsonify
import flask
from flask_cors import CORS
import socket
import threading
import json
import os
from io import BytesIO
from multiprocessing import Process
import io
from PIL import Image
# 配置全局app
app = Flask(__name__)
# 导入index中定义的所有函数
#from autotrade.server.index import *

def run_index():
    # 启动web服务器,使用多线程方式,接收所有http请求
    app.run(host='0.0.0.0', port=5000, threaded=True)

def make_new_response(data):
    res = make_response(jsonify({'code': 0, 'data': data}))
    res.headers['Access-Control-Allow-Origin'] = '*'
    res.headers['Access-Control-Allow-Method'] = '*'
    res.headers['Access-Control-Allow-Headers'] = '*'

    return res


def decode_base64(data):
    """Decode base64, padding being optional.

    :param data: Base64 data as an ASCII byte string
    :returns: The decoded byte string.

    """
    missing_padding = len(data) % 4
    if missing_padding != 0:
        data += b'='* (4 - missing_padding)
    # return base64.decodestring(data)
    return base64.b64decode(data)


@app.route("/test")
def test():
    res = "{'no':'dddd'}"
    return make_new_response(res)

CORS(app, resources=r'/*', supports_credentials=True)


basedir = os.path.abspath(os.path.dirname(__file__))


transform=transforms.Compose([
            transforms.Resize(224),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485,0.456,0.406],
                                 std=[0.229,0.224,0.225])
                            ])
@app.route('/getpic', methods=['POST'])
def getpic():
    data = json.loads(flask.request.get_data("data"))
    data_64 = str.encode(data['data'])
    print(type(data_64))

    #print(data_64)
    print('------------------------')
    print(str(data_64, 'utf8'))
    imgdata = decode_base64(data_64)
    file = open('1.jpg', 'wb')
    file.write(imgdata)
    file.close()
    image = Image.open(r"1.jpg").convert('RGB')
    image = transform(image).unsqueeze(0)
    modelme = torch.load('modefresnet.pkl')
    modelme.eval()
    outputs = modelme(image)
    _, predict = torch.max(outputs.data, 1)
    for j in range(image.size()[0]):
        print('predicted: {}'.format(class_names[predict[j]]))

    return class_names[predict[j]]
 
if __name__ == "__main__":
    app.run(debug=True)


深度学习的处理图片的网络模型就不贴了,需要的可以留言
用的是Resnet残差网络。识别速度还是很快的,判断的正确率也比较高。(训练的数据集很少,只有六百多张)

##Y1BCojf69##4;1%yBNfY3ne6a!/
下例为从指定的层提取ResNet50的特征。

import torch
from torch import nn
import torchvision.models as models
import torchvision.transforms as transforms
import cv2

class FeatureExtractor(nn.Module): # 提取特征工具
    def __init__(self, submodule, extracted_layers):
        super(FeatureExtractor, self).__init__()
        self.submodule = submodule
        self.extracted_layers = extracted_layers
 
    def forward(self, x):
        outputs = []
        for name, module in self.submodule._modules.items():
            if name is "fc": 
                x = x.view(x.size(0), -1)
            x = module(x)
            if name in self.extracted_layers:
                outputs.append(x)
        return outputs

model = models.resnet50(pretrained=True) # 加载resnet50工具
model = model.cuda()
model.eval()

img=cv2.imread('test.jpg') # 加载图片
img=cv2.resize(img,(224,224));
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
img=transform(img).cuda()
img=img.unsqueeze(0)

model2 = FeatureExtractor(model, ['layer3']) # 指定提取 layer3 层特征
with torch.no_grad():
    out=model2(img)
    print(len(out), out[0].shape)
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐