怎么编辑自己的网站,怎么用自己主机做网站_,潍坊外贸网站制作,长春市建设信息网站最近在做这个这项目奇店桶装水小程序V1.3.9安装包骑手端V2.0.1小程序前端 最近#xff0c;我在进行前端开发时#xff0c;遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需#xff0c;我一直在冥思苦想。终于有了一个解决方法…最近在做这个这项目奇店桶装水小程序V1.3.9安装包骑手端V2.0.1小程序前端 最近我在进行前端开发时遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需我一直在冥思苦想。终于有了一个解决方法——使用Canvas来处理。 这是真材实料的文章——让你的Canvas的技术更上一层楼 效果图 实现思路
1. 准备工作 视频和画布元素在HTML模板中定义了一个video标签用于播放视频以及一个canvas标签用来绘制处理后的视频帧。 初始化在组件挂载(mounted)时获取视频和画布元素并初始化绘图上下文。
templatediv classvideoBgRemove!-- 视频元素 --video refvideo loop autoplay muted stylewidth: 240px;source src/8_1736396574.mp4 typevideo/mp4Your browser does not support the video tag./video!-- 画布元素 --canvas refcanvas width200 height450/canvas/div
/templatescript
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
templatediv idapph1背景人像处理/h1VideoRemoval //div
/templatescript
import VideoRemoval from ./components/VideoRemoval.vue;export default {name: App,components: {VideoRemoval}
}
/scriptstyle
#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; /* 背景固定在视口 */
}
/style2.VideoRemoval.vue
templatediv classvideoBgRemovevideo idvideosrc/8_1736396574.mp4loopautoplaymutedrefvideostylewidth: 240px;/videocanvas idoutput-canvaswidth200height450willReadFrequentlytruerefcanvas/canvas/div
/templatescript
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的技术更上一层楼