利用 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

相关推荐
GIS程序媛—椰子18 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00125 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端28 分钟前
Content Security Policy (CSP)
前端·javascript·面试
乐闻x31 分钟前
ESLint 使用教程(一):从零配置 ESLint
javascript·eslint
木舟100931 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439142 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习