利用 canvas 2D 把视频处理成透明背景

背景

看过我这篇文章: 《使用 div 能以像素级别绘制图片吗?》 的同学们应该都知道图片的存储结构是什么样的了。

今天给大家分享一个如何使用 canvas 2D 将带有透明通道的视频处理成透明的背景。

如图所示的视频:

这种视频的内容分为左右两部分,左边是黑白内容,右边是彩色内容。目的就是要将它处理成透明的背景。

最终结果:

对于这种视频的处理办法,有另外一种处理办法,就是使用 webgl 来实现,大家对 webgl 处理办法感兴趣的话,可以前往查看这篇文章:webgl 实现透明视频 动画

原理

读取视频数据

因为我们要使用 canvas 2d 来处理视频数据,所以只能是提取出视频里的每一帧的图片数据来进行处理。

万幸的是 canvas 原生就支持这种能力:

js 复制代码
canvasContext.drawImage(videoElement, 0, 0)

你没看错,直接把 video 元素传进去就可以了。这样就把视频数据写入到了 canvas 里面。

写进去的数据就是一帧视频图片,因为视频是图片组合而成的,所以写进去的就是一帧图片数据。

解析图片数据

使用 getImageData 就能获取到写入到 canvas 中的图片数据,你将会看到里面存储的是图片的一组组 rgba 值:[0,0,0,255,123,134,23,255, ...]。它是每4 个数字组成为一个颜色值,每一组的最后一个值是 alpha 通道,255 表示不透明,0 表示透明。

因为它是一个一维数组,我们不太方便做逻辑上的处理,所以我这里会将它转换为二维数组来进行处理,如果不明白为啥能转,请查看我的上一篇文章。

处理透明度

接下来就是处理透明度了,因为这种视频的左右两部分是一样的,所以逻辑就是读取左边的像素来处理对应的右边的像素。如果左边是黑色,那就把右边的对应位置的像素设置成透明的。这样处理完毕后,就能得到最终的结果了。

代码示例

html 复制代码
<template>
  <div class="about">
    <video
      ref="video"
      src="../assets/2868412.mp4"
      autoplay="true"
      width="420"
      height="300"
      controls
      loop
      muted
    ></video>
    <hr />
    <canvas
      ref="canvas1"
      width="1500"
      height="1064"
      style="width: 420px; height: 300px"
    ></canvas>
    <canvas
      ref="canvas2"
      width="1500"
      height="1064"
      style="width: 420px; height: 300px"
    ></canvas>
  </div>
</template>

<script>
export default {
  data() {
    return {
      id: null,
      canvas1: null,
      ctx1: null,
      canvas2: null,
      ctx2: null,
    };
  },
  mounted() {
    const c = this.$refs.canvas1;
    this.ctx1 = c.getContext("2d", { willReadFrequently: true });
    const c2 = this.$refs.canvas2;
    this.ctx2 = c2.getContext("2d", { willReadFrequently: true });
    const video = this.$refs.video;
    video.addEventListener(
      "play",
      () => {
        let $this = this;
        (function loop() {
          if (!video.paused && !video.ended) {
            $this.ctx2.drawImage(video, 0, 0);
            let frameData = $this.ctx2.getImageData(0, 0, 1500, 1064);
            let data = frameData.data;
            for (let i = 0; i < 1064; i++) {
              for (let j = 0; j < 1500; j++) {
                if (j < 750) {
                  if (
                    data[i * 1500 * 4 + j * 4] === 0 &&
                    data[i * 1500 * 4 + j * 4 + 1] === 0 &&
                    data[i * 1500 * 4 + j * 4 + 2] === 0
                  ) {
                    data[i * 1500 * 4 + (j + 750) * 4 + 3] = 0;
                  }
                }
              }
            }
            $this.ctx1.putImageData(frameData, -750, 0, 750, 0, 750, 1064);
            setTimeout(loop, 1000 / 24);
          }
        })();
      },
      0
    );
  },
};
</script>

这个原视频的地址是 https://dlied5sdk.myapp.com/music/release/upload/t_mm_file_publish/2868412.mp4

对应作者的 webgl 处理方法是这个,感兴趣的可以学习: https://y.qq.com/m/act/demo/index.html

相关推荐
人工智能训练4 小时前
【极速部署】Ubuntu24.04+CUDA13.0 玩转 VLLM 0.15.0:预编译 Wheel 包 GPU 版安装全攻略
运维·前端·人工智能·python·ai编程·cuda·vllm
会跑的葫芦怪5 小时前
若依Vue 项目多子路径配置
前端·javascript·vue.js
xiaoqi9226 小时前
React Native鸿蒙跨平台如何进行狗狗领养中心,实现基于唯一标识的事件透传方式是移动端列表开发的通用规范
javascript·react native·react.js·ecmascript·harmonyos
jin1233226 小时前
React Native鸿蒙跨平台剧本杀组队消息与快捷入口组件,包含消息列表展示、快捷入口管理、快捷操作触发和消息详情预览四大核心功能
javascript·react native·react.js·ecmascript·harmonyos
烬头88218 小时前
React Native鸿蒙跨平台实现二维码联系人APP(QRCodeContactApp)
javascript·react native·react.js·ecmascript·harmonyos
pas1368 小时前
40-mini-vue 实现三种联合类型
前端·javascript·vue.js
摇滚侠8 小时前
2 小时快速入门 ES6 基础视频教程
前端·ecmascript·es6
2601_949833398 小时前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter
珑墨8 小时前
【Turbo】使用介绍
前端
军军君019 小时前
Three.js基础功能学习十三:太阳系实例上
前端·javascript·vue.js·学习·3d·前端框架·three