webrtc实现虚拟背景

上一篇实现了一对多直播模式,但在实际场景中,有时会遇到使用虚拟背景的需求。现在就看看如何在前端实现给视频流添加虚拟背景,后面再将虚拟背景和直播以及视频会议组合起来,逐步实现一个功能完整的音视频会议系统

认识虚拟背景

在生活应该都见过虚拟背景,尤其是现在微信视频通话过程中新增的模糊背景功能。这个过程还挺复杂的,整个实现逻辑涉及到人物动态计算、人像抠图、背景填充(增加马赛克或者其他的色彩)等从而才能实现模糊背景这个看似简单的功能。

但是,我们不需要做这么多事情,只需要把人家训练好的人工智能模型拿来使用即可。

实现模糊背景需要的几个核心步骤:

  1. 识别视频中的人物 (模型实现)
  2. 动态从这个视频中扣出人物画面 (模型实现)
  3. 给非人部分增加马赛克或者其他的背景 (我们实现)

两个最关键最难的步骤由模型来实现,我们只需要将模型实现出来的效果再稍微修改一下即可。只要会使用模型,不必涉及到深层次的算法学习,没必要。

这里使用谷歌开源的一个机器学习框架 MediaPipe实现虚拟背景的功能,官网地址在这里

所使用的模型是这个

利用上述框架中的图像分割模型,就可以实现我们在摄像头中的画面人物和背景分割的目标。分割完成后,还可以利用其他强大的功能,对已经分割识别的动态流自定义处理,进而实现背景自定义。

代码实现

  1. 首先在本地运行下官网示例,因为接下来就是要使用这个代码示例来完成虚拟背景。
  2. 运行没问题之后,安装依赖包 "@mediapipe/tasks-vision": "^0.10.10",然后直接复制示例中的代码到新建文件中(没有看错,就是直接复制,简单粗暴)
  3. 复制完,就相当于实现了核心步骤的前两个步骤了,再来看看第三个步骤的实现,注意到官网中的这段话

再结合代码看看:

js 复制代码
function callbackForVideo(result: ImageSegmenterResult) {
  let imageData = canvasCtx.getImageData(
    0,
    0,
    video.videoWidth,
    video.videoHeight
  ).data;
  const mask: Number[] = result.categoryMask.getAsFloat32Array();
  let j = 0;
  for (let i = 0; i < mask.length; ++i) {
    const maskVal = Math.round(mask[i] * 255.0);
    const legendColor = legendColors[maskVal % legendColors.length];
    imageData[j] = (legendColor[0] + imageData[j]) / 2;
    imageData[j + 1] = (legendColor[1] + imageData[j + 1]) / 2;
    imageData[j + 2] = (legendColor[2] + imageData[j + 2]) / 2;
    imageData[j + 3] = (legendColor[3] + imageData[j + 3]) / 2;
    j += 4;
  }
  const uint8Array = new Uint8ClampedArray(imageData.buffer);
  const dataNew = new ImageData(
    uint8Array,
    video.videoWidth,
    video.videoHeight
  );
  canvasCtx.putImageData(dataNew, 0, 0);
  if (webcamRunning === true) {
    window.requestAnimationFrame(predictWebcam);
  }
}

注意看mask数组,里面就是模型切割人物和背景之后的数据了,这个数据里用0和1来区分了背景和人物。所以我们要做的就是在这里取出人物,然后再结合自定义图片的数据,就可以生成新的具有虚拟自定义背景的视频了。

  1. 那么先需要将自定义图片通过canvas绘制,然后拿到绘制后的图片数据,代码如下:
js 复制代码
const createCustomImgData = () => {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onerror = (e) => {
      reject("加载失败" + e);
    };
    img.onload = () => {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      canvas.width = videoDom.videoWidth;
      canvas.height = videoDom.videoHeight;
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
      resolve(data); // 将获取到的 customImgData 传递给 resolve
    };
    img.src = "https://127.0.0.1:8088/src/assets/imgs/bg.jpg";
  });
};
  1. 将拿到的自定义图片数据与mask数据进行混合,就可以生成虚拟自定义背景的视频了。重点在于for循环中,代码如下:
js 复制代码
const callbackForVideo = async (result) => {
  if (!customImgData.value) {
    customImgData.value = await createCustomImgData();
  }

  let imageData = canvasCtx.getImageData(
    0,
    0,
    videoDom.videoWidth,
    videoDom.videoHeight
  ).data;
  const mask = result.categoryMask.getAsFloat32Array();
  let j = 0;
  for (let i = 0; i < mask.length; ++i) {
    if (mask[i] === 0) {
      const maskVal = Math.round(mask[i] * 255.0);
      const legendColor = legendColors[maskVal % legendColors.length];
      imageData[j] = (legendColor[0] + imageData[j]) / 2;
      imageData[j + 1] = (legendColor[1] + imageData[j + 1]) / 2;
      imageData[j + 2] = (legendColor[2] + imageData[j + 2]) / 2;
      imageData[j + 3] = (legendColor[3] + imageData[j + 3]) / 2;
    } else {
    // 这里用自定义图片数据替换原来的mask数据
      imageData[j] = customImgData.value[j];
      imageData[j + 1] = customImgData.value[j + 1];
      imageData[j + 2] = customImgData.value[j + 2];
      imageData[j + 3] = customImgData.value[j + 3];
    }
    j += 4;
  }
  const uint8Array = new Uint8ClampedArray(imageData.buffer);
  const dataNew = new ImageData(
    uint8Array,
    videoDom.videoWidth,
    videoDom.videoHeight
  );
  canvasCtx.putImageData(dataNew, 0, 0);
  if (webcamRunning.value === true) {
    window.requestAnimationFrame(predictWebcam);
  }
};

到这里,功能已经没啥问题了。我们只是实现了用自定义背景替换模型分割出来的背景,其他的都由模型来实现。总的来说还是挺简单的。当然,如果需要实现更多复杂的定制的功能,也可以在此基础上进行拓展。比如人脸识别,手势识别等等,这里的模型是可以叠加使用的,就不在此赘述了。

项目操作

项目地址

前端:gitee.com/yoboom/webr... 后端:gitee.com/yoboom/webr...

直接点开项目中的虚拟视频背景即可,属于是有手就行。

下一章节会将虚拟背景加入到一对多直播模式中

另外, 推荐一下我的另一个开源项目:问卷平台

使用的技术栈为react + ts + echarts + 高德地图 + webrtc 目前正在持续开发中。有想要学习的小伙伴可以加入进来,一起交流学习

相关推荐
丁总学Java11 分钟前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
懒羊羊大王呀22 分钟前
CSS——属性值计算
前端·css
道爷我悟了1 小时前
Vue入门-指令学习-v-html
vue.js·学习·html
无咎.lsy1 小时前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec1 小时前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec1 小时前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
工业互联网专业1 小时前
毕业设计选题:基于ssm+vue+uniapp的校园水电费管理小程序
vue.js·小程序·uni-app·毕业设计·ssm·源码·课程设计
豆豆2 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
计算机学姐2 小时前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis
twins35203 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js