如何在前端给视频进行去除绿幕并替换背景?-----Vue3!!
最近在做这个这项目奇店桶装水小程序V1.3.9安装包+骑手端V2.0.1+小程序前端
最近,我在进行前端开发时,遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需,我一直在冥思苦想。终于有了一个解决方法——使用Canvas来处理。
这是真材实料的文章——让你的Canvas的技术更上一层楼!!!


效果图
实现思路
1. 准备工作 视频和画布元素:在HTML模板中定义了一个<video>标签用于播放视频,以及一个<canvas>标签用来绘制处理后的视频帧。 初始化:在组件挂载(mounted)时,获取视频和画布元素,并初始化绘图上下文。
<template><div class="videoBgRemove"><!-- 视频元素 --><video ref="video" loop autoplay muted style="width: 240px;"><source src="/8_1736396574.mp4" type="video/mp4">Your browser does not support the video tag.</video><!-- 画布元素 --><canvas ref="canvas" width="200" height="450"></canvas></div>
</template><script>
export default {data() {return {featherStrength: 0.4, // 羽化强度控制};},mounted() {// 初始化视频和画布引用this.video = this.$refs.video;this.canvas = this.$refs.canvas;this.ctx = this.canvas.getContext('2d');this.canvas_tmp = document.createElement('canvas');this.canvas_tmp.width = this.canvas.width;this.canvas_tmp.height = this.canvas.height;this.ctx_tmp = this.canvas_tmp.getContext('2d');// 初始化其他变量this.init();},methods: {init() {// 当视频开始播放时,调用computeFrame进行逐帧处理this.video.addEventListener('play', this.computeFrame);}}
};
</script>
2. 视频帧处理逻辑 逐帧处理:当视频开始播放时,computeFrame函数会不断被调用,每次调用都会处理一帧视频数据。 临时画布:为了不影响原始视频的播放,所有图像处理都在一个临时创建的画布(canvas_tmp)上进行。 图像数据获取:从临时画布上获取当前帧的图像数据(像素信息)以进行处理。
methods: {computeFrame() {if (!this.video || this.video.paused || this.video.ended) return;// 绘制当前帧到临时画布上this.ctx_tmp.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);// 获取当前帧的图像数据let frame = this.ctx_tmp.getImageData(0, 0, this.canvas.width, this.canvas.height);// 后续处理...}
}
3. 背景移除 颜色检测:假设背景为特定的颜色(例如绿色),对于每个像素点,如果其RGB值符合预设的背景颜色范围,则将其alpha通道设置为0,即变为透明。
methods: {computeFrame() {// ... (前面的代码)const pointLens = frame.data.length / 4;// 遍历每一个像素点for (let i = 0; i < pointLens; i++) {let r = frame.data[i * 4];let g = frame.data[i * 4 + 1];let b = frame.data[i * 4 + 2];// 假设背景是绿色,将符合条件的像素设置为透明if (r < 100 && g > 120 && b < 200) { frame.data[i * 4 + 3] = 0; // 设置alpha通道为0,使背景透明}}// 后续处理...}
}
4. 羽化效果 边缘检测与平均:对于非透明的像素,计算它周围的像素,取周围像素颜色的平均值作为新颜色,并根据周围的透明度调整当前像素的透明度,以此来实现羽化效果。 强度控制:通过featherStrength参数可以控制羽化的程度,从而让边缘过渡更加自然。
methods: {computeFrame() {// ... (前面的代码)// 创建一个临时的数据副本,避免修改原始数据const tempData = [...frame.data];// 对非透明像素应用羽化效果for (let i = 0; i < pointLens; i++) {if (frame.data[i * 4 + 3] === 0) continue; // 忽略已经透明的像素// 计算当前像素的位置let [row, col] = this.numToPoint(i + 1, frame.width);// 获取周围的像素点let aroundPoints = this.getAroundPoint([row, col], frame.width, frame.height, 3);// 计算周围非透明像素的颜色平均值let opNum = 0;let rSum = 0;let gSum = 0;let bSum = 0;aroundPoints.forEach(([pRow, pCol]) => {let index = this.pointToNum([pRow, pCol], frame.width);rSum += tempData[(index - 1) * 4];gSum += tempData[(index - 1) * 4 + 1];bSum += tempData[(index - 1) * 4 + 2];if (tempData[(index - 1) * 4 + 3] !== 255) opNum++;});// 计算新的alpha值let alpha = (255 / aroundPoints.length) * (aroundPoints.length - opNum);// 根据羽化强度调整alphaif (alpha !== 255) {frame.data[i * 4] = parseInt(rSum / aroundPoints.length);frame.data[i * 4 + 1] = parseInt(gSum / aroundPoints.length);frame.data[i * 4 + 2] = parseInt(bSum / aroundPoints.length);frame.data[i * 4 + 3] = parseInt(alpha * this.featherStrength);}}// 将处理后的图像数据绘制到实际显示的画布上this.ctx.putImageData(frame, 0, 0);// 持续循环requestAnimationFrame(this.computeFrame);},numToPoint(num, width) {let col = num % width;let row = Math.floor(num / width);return [row + 1, col === 0 ? width : col];},pointToNum(point, width) {let [row, col] = point;return (row - 1) * width + col;},getAroundPoint(point, width, height, area) {let [row, col] = point;let allAround = [];for (let i = -Math.floor(area / 2); i <= Math.floor(area / 2); i++) {for (let j = -Math.floor(area / 2); j <= Math.floor(area / 2); j++) {if (i === 0 && j === 0) continue; // 跳过中心点let pRow = row + i;let pCol = col + j;if (pRow > 0 && pCol > 0 && pRow <= height && pCol <= width) {allAround.push([pRow, pCol]);}}}return allAround;}
}
5. 显示处理结果 更新画布:将处理后的图像数据应用到实际显示的画布(canvas)上,这样用户就能看到带有透明背景和羽化效果的视频了。
// 将处理后的图像数据绘制到实际显示的画布上
this.ctx.putImageData(frame, 0, 0);
6. 持续循环 递归调用:computeFrame函数会在每一帧处理完毕后立即再次调用自己,形成一个持续的循环,直到视频停止播放。代码加在这里面。
methods: {computeFrame() {// ... (前面的代码)// 持续循环requestAnimationFrame(this.computeFrame);}
}
完整的demo
1.App.vue
<template><div id="app"><h1>背景人像处理</h1><VideoRemoval /></div>
</template><script>
import VideoRemoval from './components/VideoRemoval.vue';export default {name: 'App',components: {VideoRemoval}
}
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;background-image: url("../src/assets/web_bg.jpg"); /* 使用正确的路径 */background-size: cover; /* 背景图片覆盖整个容器 */background-position: center center; /* 背景图片居中显示 */background-repeat: no-repeat; /* 防止背景图片重复 */background-attachment: fixed; /* 背景固定在视口 */
}
</style>
2.VideoRemoval.vue
<template><div class="videoBgRemove"><video id="video"src="/8_1736396574.mp4"loopautoplaymutedref="video"style="width: 240px;"></video><canvas id="output-canvas"width="200"height="450"willReadFrequently="true"ref="canvas"></canvas></div>
</template><script>
export default {data () {return {video: null,canvas: null,ctx: null,canvas_tmp: null,ctx_tmp: null,featherStrength: 0.4, // 羽化强度控制};},methods: {init () {this.ctx = this.canvas.getContext('2d');this.canvas_tmp = document.createElement('canvas');this.canvas_tmp.setAttribute('width', 200);this.canvas_tmp.setAttribute('height', 450);this.ctx_tmp = this.canvas_tmp.getContext('2d');this.video.addEventListener('play', this.computeFrame);},numToPoint (num, width) {let col = num % width;let row = Math.floor(num / width);row = col === 0 ? row : row + 1;col = col === 0 ? width : col;return [row, col];},pointToNum (point, width) {let [row, col] = point;return (row - 1) * width + col;},getAroundPoint (point, width, height, area) {let [row, col] = point;let allAround = [];if (row > height || col > width || row < 0 || col < 0) return allAround;for (let i = 0; i < area; i++) {let pRow = row - 1 + i;for (let j = 0; j < area; j++) {let pCol = col - 1 + j;if (i === area % 2 && j === area % 2) continue;allAround.push([pRow, pCol]);}}return allAround.filter(([iRow, iCol]) => {return iRow > 0 && iCol > 0 && iRow <= height && iCol <= width;});},computeFrame () {if (this.video) {if (this.video.paused || this.video.ended) return;}this.ctx_tmp.drawImage(this.video, 0, 0, this.video.clientWidth, this.video.clientHeight);let frame = this.ctx_tmp.getImageData(0, 0, this.video.clientWidth, this.video.clientHeight);const height = frame.height;const width = frame.width;const pointLens = frame.data.length / 4;// 背景透明化(假设背景为特定颜色,这里选择绿色)for (let i = 0; i < pointLens; i++) {let r = frame.data[i * 4];let g = frame.data[i * 4 + 1];let b = frame.data[i * 4 + 2];if (r < 100 && g > 120 && b < 200) {frame.data[i * 4 + 3] = 0;}}const tempData = [...frame.data];for (let i = 0; i < pointLens; i++) {if (frame.data[i * 4 + 3] === 0) continue;const currentPoint = this.numToPoint(i + 1, width);const arroundPoint = this.getAroundPoint(currentPoint, width, height, 3);let opNum = 0;let rSum = 0;let gSum = 0;let bSum = 0;arroundPoint.forEach((position) => {const index = this.pointToNum(position, width);rSum += tempData[(index - 1) * 4];gSum += tempData[(index - 1) * 4 + 1];bSum += tempData[(index - 1) * 4 + 2];if (tempData[(index - 1) * 4 + 3] !== 255) opNum++;});let alpha = (255 / arroundPoint.length) * (arroundPoint.length - opNum);// 调整羽化效果if (alpha !== 255) {frame.data[i * 4] = parseInt(rSum / arroundPoint.length);frame.data[i * 4 + 1] = parseInt(gSum / arroundPoint.length);frame.data[i * 4 + 2] = parseInt(bSum / arroundPoint.length);// 根据羽化强度调整 alphaframe.data[i * 4 + 3] = parseInt(alpha * this.featherStrength);}}this.ctx.putImageData(frame, 0, 0);setTimeout(this.computeFrame, 0);}},mounted () {this.video = this.$refs.video;this.canvas = this.$refs.canvas;this.init();}
};
</script>
完整项目demo在前端给视频去除绿幕并替换背景: 最近,我在进行前端开发时,遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需,我一直在冥思苦想。终于有了一个解决方法——使用Canvas来处理。 这是真材实料的文章——让你的Canvas的技术更上一层楼!!!
相关文章:
如何在前端给视频进行去除绿幕并替换背景?-----Vue3!!
最近在做这个这项目奇店桶装水小程序V1.3.9安装包骑手端V2.0.1小程序前端 最近,我在进行前端开发时,遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需,我一直在冥思苦想。终于有了一个解决方法…...
使用中间件自动化部署java应用
为了实现你在 IntelliJ IDEA 中打包项目并通过工具推送到两个 Docker 服务器(172.168.0.1 和 172.168.0.12),并在推送后自动或手动重启容器,我们可以按照以下步骤进行操作: 在 IntelliJ IDEA 中配置 Maven 或 Gradle 打…...
pytorch张量分块投影示例代码
张量的投影操作 背景 张量投影 是深度学习中常见的操作,将输入张量通过线性变换映射到另一个空间。例如: Y=W⋅X+b 其中: X: 输入张量(形状可能为 (B,M,K),即批量维度、序列维度、特征维度)。W: 权重矩阵((K,N),将 K 维投影到 N 维)。b: 偏置向量(可选,(N,))。Y:…...
Visual Studio 同一解决方案 同时运行 多个项目
方案一 方案二...
VMware中Ubuntu如何连接网络?安排!
一、设置NAT模式 1、关闭Ubuntu虚拟机: 确保Ubuntu已经完全关机,而不是挂起或休眠状态。 2、编辑虚拟网络设置: 在VMware主界面点击“编辑”菜单,选择“虚拟网络编辑器”。 如果需要,选择VMnet8 (NAT模式)并点击“更改…...
使用 Charles 调试 Flutter 应用中的 Dio 网络请求
为了成功使用 Charles 抓取并调试 Flutter 应用程序通过 Dio 发起的网络请求,需遵循特定配置步骤来确保应用程序能够识别 Charles 的 SSL 证书,并正确设置代理服务器。 配置 Charles 以支持 HTTPS 请求捕获 Charles 默认会拦截 HTTP 流量;…...
CMD批处理命令入门(6)——常用的特殊字符
CMD批处理命令入门(6)——特殊字符 本章内容主要学习要点:重定向符 >、>>命令管道符 |组合命令 &、&&、||转义字符 ^变量引导符 %界定符 "" 本章内容主要学习要点: >、>>重定向符| 命令…...
【跟着官网学技术系列之MySQL】第7天之创建和使用数据库1
前言 在当今信息爆炸的时代,拥有信息检索的能力很重要。 作为一名软件工程师,遇到问题,你会怎么办?带着问题去搜索引擎寻找答案?亦或是去技术官网,技术社区去寻找? 根据个人经验,一…...
next-auth v5 结合 Prisma 实现登录与会话管理
1. 安装依赖 npm install next-auth prisma prisma/client2. 配置 Prisma 模型 在 prisma/schema.prisma 文件中定义 User 和 Account 模型(next-auth v5 推荐使用自定义模型)。 model User {id String id default(uuid())email …...
WPS excel使用宏编辑器合并 Sheet工作表
使用excel自带的工具合并Sheet表,我们会发现需要开通WPS会员才能使用合并功能; 那么WPS excel如何使用宏编辑器进行合并 Sheet表呢? 1、首先我们要看excel后缀是 .xlsx 还是 .xls ;如果是.xlsx 那么 我们需要修改为 .xls 注…...
(即插即用模块-Attention部分) 四十四、(ICIP 2022) HWA 半小波注意力
文章目录 1、Half Wavelet Attention2、代码实现 paper:HALFWAVELET ATTENTION ON M-NET FOR LOW-LIGHT IMAGE ENHANCEMENT Code:https://github.com/FanChiMao/HWMNet 1、Half Wavelet Attention 传统的图像增强方法主要关注图像在空间域的特征信息&am…...
Linux第二课:LinuxC高级 学习记录day04
6、shell中的语句 6.3、结构性语句 6.3.1、if if…then…fi 1、结构 1)基本结构 if 表达式 then 命令表 fi if [ 表达式 ] // 【】两侧有空格 then 命令表 fi 2)分层结构 if 表达式 then 命令表1 else 命令表2 fi 3)嵌套结构 if …...
occ的开发框架
occ的开发框架 1.Introduction This manual explains how to use the Open CASCADE Application Framework (OCAF). It provides basic documentation on using OCAF. 2.Purpose of OCAF OCAF (the Open CASCADE Application Framework) is an easy-to-use platform for ra…...
Redis 如何解决大 key 问题
前言 嗨👋,大家好,我是雪荷。做为一个后端开发,Redis 是我们经常接触到的一个非关系行数据库。其对我们系统开发和优化有着举足轻重的作用,但是随着业务和用户迅速增长,也会滋生许多的问题,而大…...
驱动开发系列33 - Linux Graphics mesa Intel驱动介绍
一:概述 mesa 中的 Intel 驱动体系是为支持 Intel GPU 提供图形 API 的硬件实现部分,主要包括 OpenGL、Vulkan等图形接口,Intel驱动实现整体上分为四层: 第一层:API 层, 实现 OpenGL 和 Vulkan 接口, src/mesa/main、src/vulkan。 第二层:驱动层,实现 OpenGL 和 Vulkan…...
【华为OD-E卷 - 整数编码 100分(python、java、c++、js、c)】
【华为OD-E卷 - 整数编码 100分(python、java、c、js、c)】 题目 实现一种整数编码方法,使得待编码的数字越小,编码后所占用的字节数越小。 编码规则如下: 编码时7位一组,每个字节的低7位用于存储待编码数字的补码 字…...
vue3 uniapp封装一个瀑布流组件
新增组件m-waterfall 这样就可以在页面直接使用 不用在引入了 <template><view class"m-waterfall"><view id"m-left-column" class"m-column"><slot name"left" :leftList"leftList"></slot&…...
Android Room 持久化库的介绍及使用方法
Android Room 是 Android Jetpack 组件之一,是 Google 官方推出的用于简化 SQLite 数据库操作的持久化库。它提供了一个抽象层,允许开发者在 SQLite 数据库上执行常见的 CRUD 操作,同时处理数据库连接、数据迁移和查询优化等底层细节。 Andr…...
Go语言中http.Transport的Keep-Alive配置与性能优化方法
在Go语言中,http.Transport是一个用于发送HTTP或HTTPS请求的客户端工具,它提供了许多可配置的参数以优化性能。其中,Keep-Alive配置是性能优化的关键部分。以下是对http.Transport的Keep-Alive配置与性能优化方法的详细解释: 一、…...
设计模式03:行为型设计模式之策略模式的使用情景及其基础Demo
1.策略模式 好处:动态切换算法或行为场景:实现同一功能用到不同的算法时和简单工厂对比:简单工厂是通过参数创建对象,调用同一个方法(实现细节不同);策略模式是上下文切换对象,调用…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
