媒体流数据获取和人脸检测

背景

因为最近这些年互联网面向的受众面愈来愈光,有了更多的人和设备访问互联网,这也就意味着咱们要作的就愈来愈多, 不在是像之前那样只须要调整页面样式和切图就能够了。因此就有了这篇文章的诞生,本次讲的如何在本地唤醒相机而后进行拍照,同时对拍照的照片进行修改,还能够对摄像头所捕捉的数据进行一个转发,同时还能够检测摄像头中是否存在人。vue

关键字API以及兼容性

唤醒摄像头主要用的API是 navigator.mediaDevices.getUserMedia。入参是一个对象{ audio: true, video: true } 是判断是否启动摄像头和录音,返回一个promise对象。之前的navigator.getUserMedia已经被废弃。若是video没有设置自动播放 须要在成功回调函数那里手动调用执行,只有这样摄像头才会被执行,否则就不会正确显示。git

目前 IOS 设备的微信和 Safari 均不支持,较新的安卓和桌面端浏览器均支持。另外,出于安全问题考虑,Chrome 只支持 HTTPS 页面启用摄像头。github

开启本地相机

test是用vue来的。由于考虑到到时候直接复制就好,因此在用的时候须要考虑本身的环境来进行部分修改和调整web

HTML部分
  
  <div class="contentarea">
    <div class="camera">
      <video ref="video" autoplay muted @canplay="canplay"></video>
      <button ref="startbutton" id="startbutton" @click="takepicture">Take photo</button>
    </div>
    <canvas ref="canvas"></canvas>
    <div class="output">
      <img ref="photo">
    </div>
  </div>
  
  CSS部分
  
    #photo {
      border: 1px solid black;
      width: 320px;
      height: 240px;
    }
    
    #canvas {
      display: none;
    }
    
    .camera {
      width: 340px;
      display: inline-block;
    }
    
    .output {
      width: 340px;
      display: inline-block;
    }
    
    #startbutton {
      display: block;
      position: relative;
      margin-left: auto;
      margin-right: auto;
      bottom: 32px;
      background-color: rgba(0, 150, 0, 0.5);
      border: 1px solid rgba(255, 255, 255, 0.7);
      box-shadow: 0px 0px 1px 2px rgba(0, 0, 0, 0.2);
      font-size: 14px;
      font-family: "Lucida Grande", "Arial", sans-serif;
      color: rgba(255, 255, 255, 1);
    }
    
    .contentarea {
      font-size: 16px;
      font-family: "Lucida Grande", "Arial", sans-serif;
      width: 760px;
    }
    
    JS部分
    
    // 监听摄像头是否启动成功而且按照4:3的比例初始化摄像头和画布
    canplay () {
      let { video, canvas } = this.$refs
      if (!this.streaming) {
        this.height = video.videoHeight / (video.videoWidth / this.width);
        console.log(this.height)
        if (isNaN(this.height)) {
          this.height = this.width / (4 / 3);
        }
        video.setAttribute("width", this.width);
        video.setAttribute("height", this.height);
        canvas.setAttribute("width", this.width);
        canvas.setAttribute("height", this.height);
        this.streaming = true;
      }
    },
    // 获取摄像头被点击的那一刻的数据而且存到canvas里面以便进行修改 而后在存储在img标签显示在页面
    takepicture () {
      let { video, canvas, photo } = this.$refs
      let context = canvas.getContext("2d")
      if (this.width && this.height) {
        canvas.width = this.width;
        canvas.height = this.height;
        context.drawImage(video, 0, 0, this.width, this.height);
        let data = canvas.toDataURL("image/png");
        photo.setAttribute("src", data);
      } else {
        this.clearphoto();
      }
    },
    // 清除canvas信息
    clearphoto () {
      let { canvas, photo } = this.$refs
      var context = canvas.getContext("2d");
      context.fillStyle = "#AAA";
      context.fillRect(0, 0, canvas.width, canvas.height);

      var data = canvas.toDataURL("image/png");
      photo.setAttribute("src", data);
    },
    // 初始化摄像头调用
    startup() {
      let { video } = this.$refs
      this.width = 320
      this.height = 0
      this.streaming = false
      navigator.mediaDevices
        .getUserMedia({ video: true, audio: false })
        .then((stream) => {
          video.srcObject = stream;
          video.play();
        })
        .catch((err) => {
          console.log("An error occurred: " + err);
        })
    }

autoplay 须要开启自动播放否则须要在成功的回调里面手动执行,否则不会显示内容. 若是只是开启摄像头部分咱们不须要声音,因此关闭掉了canvas

<video ref="video" autoplay muted @canplay="canplay"></video>

咱们须要等页面加载以后手动开启摄像头方法。api

mounted() {
   this.startup()
}

startup() {
  let { video } = this.$refs // 获取页面上面的视频元素
  this.width = 320 // 初始化宽度
  this.height = 0 // 初始化高度
  this.streaming = false // 初始化是否进行中
  navigator.mediaDevices
    .getUserMedia({ video: true, audio: false }) // 这里咱们只开启摄像头
    .then((stream) => { // 成功的执行
      video.srcObject = stream;
    })
    .catch((err) => { // 失败的执行
      console.error("An error occurred: " + err);
    })
},

当摄像头被成功启动以后, 就会执行 canplay 方法promise

canplay () {
  let { video, canvas } = this.$refs // 获取节点 
  if (!this.streaming) { // 是不是在运行中 若是是
    this.height = video.videoHeight / (video.videoWidth / this.width); // 初始化高度
    console.log(this.height)
    if (isNaN(this.height)) { // 这里初始化高度是为了和宽度进行一个响应 初始成一个4:3的比例
      this.height = this.width / (4 / 3);
    }
    video.setAttribute("width", this.width); // 这个时候开始设置节点的宽度和高度
    video.setAttribute("height", this.height);
    canvas.setAttribute("width", this.width);
    canvas.setAttribute("height", this.height);
    this.streaming = true;
  }
}

这个时候摄像头的基本也就已经操做完毕了 若是没有一场的时候就能够在页面上看到你摄像头所捕捉到的内容 这个时候咱们须要把你想要捕捉的内容转成图片显示到页面上浏览器

takepicture () { // 点击方法 当咱们点击 Take photo 按钮的时候 这个时候把这部分的信息转成图片
  let { video, canvas, photo } = this.$refs // 获取节点
  let context = canvas.getContext("2d")
  if (this.width && this.height) { // 把video的数据存入canvas以便咱们进行其余操做 转成图片资源而后显示在页面上
    canvas.width = this.width;
    canvas.height = this.height;
    context.drawImage(video, 0, 0, this.width, this.height);
    let data = canvas.toDataURL("image/png");
    photo.setAttribute("src", data);
  } else { // 若是初始化的宽高不存在 这个时候说明有异常须要排除,同时清除掉画布上面的图片数据
    this.clearphoto();
  }
},
clearphoto () { // 清除画布图片数据 同时更新img
  let { canvas, photo } = this.$refs
  var context = canvas.getContext("2d");
  context.fillStyle = "#AAA";
  context.fillRect(0, 0, canvas.width, canvas.height);

  var data = canvas.toDataURL("image/png");
  photo.setAttribute("src", data);
},

对摄像头捕捉的画面进行获取和解析

应用场景

完成以上步骤以后,没有问题。咱们就能够唤醒链接的摄像头设备,捕捉你想要的画面存到你想要存的地方。当咱们把捕捉到的画面存到canvas里面去以后,咱们能够进行不少操做,能够对你捕捉的画面增长你想要的特效,这个时候canvas可以实现的功能均可以在你捕捉到画面上面实现,而且转成图片。
此时,摄像头所捕捉到的内容仍是单向的,没法与别人通信。若是你想实现一种相似于视频聊天的功能。这个时候就要改一下咱们的代码,参考一下webRTC即时通信协议。须要搭建一个第三方的信令服务器,来进行双向通信。安全

人脸检测工具的使用

我如今借助的是的face-api这款工具 有捕捉人脸,检测面部特征。这个包能够把你想要检测的图片视频以及摄像头上面的内容都是支持捕捉人脸和检测面部特征。我这里主要实现的是摄像头捕捉人脸和检测面部特征。服务器

官网: https://github.com/justadudew...

HTML部分

<video id="video" autoplay muted></video>

JS部分

const video = document.getElementById("video"); // 获取节点
Promise.all([ // 加载我想要使用的模型 同时对应的josn文件和shard文件要处在同一目录 否则读取的时候可能读取不到。当你读取不到的时候你可能会报 SyntaxError: Unexpected token < in JSON at position 0。这点略坑
  faceapi.nets.tinyFaceDetector.loadFromUri("/models"),
  faceapi.nets.faceLandmark68Net.loadFromUri("/models"),
  faceapi.nets.faceRecognitionNet.loadFromUri("/models"),
  faceapi.nets.faceExpressionNet.loadFromUri("/models")
]).then(startVideo); // 载入成功以后唤醒摄像头

function startVideo() {
  navigator.getUserMedia(
    { video: {} },
    stream => (video.srcObject = stream),
    err => console.error(err)
  );
}

video.addEventListener("play", () => { // 当摄像头成功启动以后 开始执行这部分的方法
  const canvas = faceapi.createCanvasFromMedia(video); // 建立一个画布
  document.body.append(canvas);
  const displaySize = { width: 640, height: 480 }; // 这部分的大小是等同video的大小的
  faceapi.matchDimensions(canvas, displaySize); // 声明大小
  setInterval(async () => { // 咱们须要持续传递摄像头的数据
    const detections = await faceapi
      .detectAllFaces(video, new faceapi.TinyFaceDetectorOptions())
      .withFaceLandmarks()
      .withFaceExpressions();
    const resizedDetections = faceapi.resizeResults(
      detections,
      displaySize
    );
    canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
    faceapi.draw.drawDetections(canvas, resizedDetections);
    faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);
    faceapi.draw.drawFaceExpressions(canvas, resizedDetections);
  }, 100);
});

场景

相关文章
相关标签/搜索