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

相关推荐
gqkmiss7 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃12 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰17 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye23 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm25 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You1 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js