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

相关推荐
勿语&26 分钟前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈27 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水1 小时前
简洁之道 - React Hook Form
前端
正小安3 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常5 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式