原生Js Canvas去除视频绿幕背景

这篇具有很好参考价值的文章主要介绍了原生Js Canvas去除视频绿幕背景。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Js去除视频背景


注: 这里的去除视频背景并不是对视频文件进行操作去除背景

如果需要对视频扣除背景并导出可以使用ffmpeg等库,这里仅作播放用所以采用这种方法

由于uniapp中的canvas经过封装,且 uniapp 的 drawImage 无法绘制视频帧画面,因此uniapp中不适用


实现过程是将视频使用canvas逐帧截下来对截取的图片进行处理,然后在canvas中显示处理好的图片

最后通过定时器高速处理替换,形成视频播放的效果,效果如下图⬇

原生Js Canvas去除视频绿幕背景,Media,javascript,音视频

边缘仍然会有些绿幕的像素,可以通过其他的处理进行优化


原理

首先使用canvas的 drawImage 方法将video的当前帧画面绘制到canvas中

然后再通过 getImageData 方法获取当前canvas的所有像素的rgba值组成的数组

获取到的值为[r,g,b,a,r,g,b,a,...],每一组rgba的值就是一个像素,所以获取到的数组长度是canvas的像素的数量 * 4

通过判断每一组rgb的值是否为绿幕像素,然后设置其透明通道的alpha的值为0实现效果


代码

因为canvas会受到跨域的影响导致画布被污染,因此首先需要将测试视频下载到本地

测试视频 地址

<template>
    <div class="videoBgRemove">
        <video id="video" src="/images/example.mp4" loop autoplay muted ref="video" style="width: 240px;height: 135px;"></video>
        <canvas id="output-canvas" width="240" height="135" willReadFrequently="true" ref="canvas"></canvas>
    </div>
</template>

<script setup>
import {ref, onMounted} from 'vue';

const video = ref(null);
const canvas = ref(null);
const ctx = ref(null);
const canvas_tmp = ref(null);
const ctx_tmp = ref(null);

const init = () => {
    ctx.value = canvas.value.getContext('2d');

    // 创建的canvas宽高最好与显示图片的canvas、video宽高一致
    canvas_tmp.value = document.createElement('canvas');
    canvas_tmp.value.setAttribute('width', 240);
    canvas_tmp.value.setAttribute('height', 135);
    ctx_tmp.value = canvas_tmp.value.getContext('2d');

    video.value.addEventListener('play', computeFrame);
}

const computeFrame = () => {
    if (video.value) {
        if (video.value.paused || video.value.ended) return;
    }
    // 如果视频比例和canvas比例不正确可能会出现显示形变, 调整除的值进行比例调整
    ctx_tmp.value.drawImage(video.value, 0, 0, video.value.clientWidth / 1, video.value.clientHeight / 1);

    // 获取到绘制的canvas的所有像素rgba值组成的数组
    let frame = ctx_tmp.value.getImageData(0, 0, video.value.clientWidth, video.value.clientHeight);

    // 共有多少像素点
    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];

        // 判断如果rgb值在这个范围内则是绿幕背景,设置alpha值为0 
        // 同理不同颜色的背景调整rgb的判断范围即可
        if (r < 100 && g > 120 && b < 200) {
            frame.data[i * 4 + 3] = 0;
        }
    }

    // 重新绘制到canvas中显示
    ctx.value.putImageData(frame, 0, 0);
    // 递归调用
    setTimeout(computeFrame, 0);
}

onMounted(() => {
    init();
})
</script>

可以看到边缘仍有绿色像素闪烁,使用算法进行处理的话效果会更好,但相应的资源的消耗也会提升,造成帧率下降。

下面展示通过一些算法进行羽化和颜色过渡。

优化

羽化

通过上述 rgb 值的筛选后仍有一些绿幕像素由于 rgb 值与人物颜色相近而无法处理,

扩大 rgb 值的筛选范围又导致人物像素出现镂空,因此我们需要对边缘的像素进行处理。

  1. 获取处理像素的 3x3 范围内的像素
假设 x 为我们需要处理的像素值, 获取周围的所有像素 -> 1, 2, 3, 4, 6, 7, 8, 9
[
    [1, 2, 3],
    [4, x, 6],
    [7, 8, 9],
]
  1. 计算周围所有像素中透明通道为 0 的个数
假设透明通道为 0 的是 1, 2, 3
[
    [0  , 0  ,  0 ],
    [255, x  , 255],
    [255, 255, 255],
]
  1. 重新计算处理像素的 alpha

由于 x 周围的透明像素有3个,那么 xalpha 值为 (255 / 8) * (8 - 3)

相当于 x 周围有几个像素就把 255 分为几份,周围每有一个像素的 alpha 为 0, 就减一份。

计算完之后把结果赋给 xalpha 值。

注意:

由于遍历时 前一个像素的修改 会影响 后一个像素获取周围的值。

对 x 的修改会影响 y 的计算
[
    [1, 2, 3 , 4 ],
    [5, x, y , 8 ],
    [9, 1, 11, 12],
]

因此需要将第一次 rgb 筛选后的数据做一次深拷贝,获取的值基于拷贝后的值。


颜色过渡

在计算 alpha 值的时候将周围像素的 rgb 各个通道的值求和计算出平均值,

修改处理像素的 alpha 值时连带 rgb 值一起修改。

最终处理结果如下
原生Js Canvas去除视频绿幕背景,Media,javascript,音视频文章来源地址https://www.toymoban.com/news/detail-707125.html


代码(优化)

<template>
    <div class="videoBgRemove">
        <video id="video" src="/images/example.mp4" loop autoplay muted ref="video" style="width: 240px;height: 135px;"></video>
        <canvas id="output-canvas" width="240" height="135" willReadFrequently="true" ref="canvas"></canvas>
    </div>
</template>

<script setup>
import {ref, onMounted} from 'vue';

const video = ref(null);
const canvas = ref(null);
const ctx = ref(null);
const canvas_tmp = ref(null);
const ctx_tmp = ref(null);

const init = () => {
    ctx.value = canvas.value.getContext('2d');

    // 创建的canvas宽高最好与显示图片的canvas、video宽高一致
    canvas_tmp.value = document.createElement('canvas');
    canvas_tmp.value.setAttribute('width', 240);
    canvas_tmp.value.setAttribute('height', 135);
    ctx_tmp.value = canvas_tmp.value.getContext('2d');

    video.value.addEventListener('play', computeFrame);
}

const 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];
}

const pointToNum = (point, width) => {
    let [row, col] = point;
    return (row - 1) * width + col
}

const 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);
    })
}


const computeFrame = () => {
    if (video.value) {
        if (video.value.paused || video.value.ended) return;
    }
    // 如果视频比例和canvas比例不正确可能会出现显示形变, 调整除的值进行比例调整
    ctx_tmp.value.drawImage(video.value, 0, 0, video.value.clientWidth / 1, video.value.clientHeight / 1);

    // 获取到绘制的canvas的所有像素rgba值组成的数组
    let frame = ctx_tmp.value.getImageData(0, 0, video.value.clientWidth, video.value.clientHeight);

    //----- emergence ----------
    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 = numToPoint(i + 1, width);
        const arroundPoint = getAroundPoint(currentPoint, width, height, 3);
        let opNum = 0;
        let rSum = 0;
        let gSum = 0;
        let bSum = 0;
        arroundPoint.forEach((position) => {
            const index = pointToNum(position, width);
            rSum = rSum + tempData[(index - 1) * 4];
            gSum = gSum + tempData[(index - 1) * 4 + 1];
            bSum = 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) {
            // debugger
            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);
            frame.data[i * 4 + 3] = parseInt(alpha);
        }
    }

    //------------------------
    ctx.value.putImageData(frame, 0, 0);
    setTimeout(computeFrame, 0);
}

onMounted(() => {
    init();
})
</script>

到了这里,关于原生Js Canvas去除视频绿幕背景的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一键去除图片背景——background-removal-js

    一些JavaScript库和工具可以帮助实现背景去除: OpenCV.js:OpenCV的JavaScript版本,提供了许多计算机视觉功能,包括背景去除。 Jimp:一个用于处理图像的JavaScript库,提供了许多图像处理功能,包括背景去除。 Canvas:HTML5的Canvas API可用于在JavaScript中绘制和处理图像,包括背景去

    2024年01月23日
    浏览(57)
  • 怎么去除视频里的背景音乐?其实非常简单!

    如何去除视频背景音乐?在视频处理中,有时我们需要从视频中提取声音并进行处理,而不仅仅是简单地去除整个背景音乐。我们可能需要有选择性地去除人声或背景音乐。这个处理过程对于选用合适的工具至关重要。在本文中,我将分享两种可用于去除视频背景音乐的工具

    2024年02月14日
    浏览(46)
  • 原生js使用canvas实现鼠标绘制直线

    目录 目录 一、原理 二、具体实现 (1)、创建画布 (2)、获取鼠标位置 (3)、创建线段类 (4)、创建主绘制类 (5)、绘制 (6)、效果图  三、源代码 1、原生js 2、vue3实现  直线可以看成是一小段一小段的线段组成,并且两点确定一条直线; 首先当鼠标左键点击时候

    2024年02月12日
    浏览(51)
  • js 视频水印去除

    参考文档:http://txapi.cn/api 收费 识别要解析的类型(需要收费) 不收费:(这边只说下大概实现思路,具体操作没试过,因为我们这边是通过接口python来实现的) 抖音去水印 逻辑: 通过短链获取到视频mid,通过mid获取到视频详情,再通过视频详情拿到视频地址,将有水印替

    2024年02月14日
    浏览(48)
  • uniapp中使用原生canvas标签绘制视频帧来模拟拍照,拍照后将图绘制在另外一个canvas上编辑画图,这样反复操作

    uniapp中使用原生canvas标签绘制视频帧来模拟拍照,拍照后将图绘制在另外一个canvas上编辑画图,这样反复操作会导致ios系统上白屏,canvas2d上下文为null,经查阅找到相关资料 IOS 创建Canvas过多导致getContext(‘2d’) 返回null 总 Canvas 内存使用超过最大限制 (Safari 12) 从一个 bug 中延伸

    2024年02月10日
    浏览(45)
  • 纯前端--原生js将html页面变成pdf文件(html2canvas+jsPDF)

    1、将文档放在本地,用原生js进行引用和使用。 ① 新建一个名为 html2canvas.min.js 的文件,并且将线上的内容进行复制。 ② 引入 js 文件: 2、使用 npm 进行安装使用: 待续。。。 github 中文网站 CDN Jspdf.es.js:ES 2015 模块格式。 Jspdf.umd.js:UMD模块格式,用于 AMD 或脚本标签加载

    2024年02月08日
    浏览(61)
  • 原生JavaScript实现video视频控制栏

    最终预览效果:

    2024年02月03日
    浏览(38)
  • 【JavaScript】原生js实现省市区联动效果

    😉博主:初映CY的前说(前端领域) ,📒本文核心:用原生js实现省市区联动 【前言】今日在复习省市县三级联动的时候,有点忘了原生的js应该怎么样处理省市县的联动,特此写下来再次复习下 1.获取相对应的DOM对象 2.写省市县接口获取到接口信息 3.写下change事件,有变化时调

    2023年04月24日
    浏览(54)
  • 原生JS实现视频截图

    要用原生js实现视频截图,可以利用 canvas 的绘图功能 ctx.drawImage ,只需要获取到视频标签,就可以通过 drawImage 把视频当前帧图像绘制在canvas画布上。 接下来,需要把画布转化为图片,canvas提供了两个2D转换为图片的方法: canvas.toDataURL() 和 canvas.toBlob() canvas.toDataURL(mimeType,

    2024年02月05日
    浏览(45)
  • 原生js实现简单的视频播放控件

    HTML 5 视频/音频参考手册 https://www.w3school.com.cn/html5/html5_ref_audio_video_dom.asp 本文主要依靠HTML 5 api ,所有用的HTML 5 api 的使用和各项信息请参考以上链接! 如果你打算参考本文,这里所用的视频什么的请自行准备。这里仅建议初学者参考。 因为主要依靠HTML 5的api,所有也没什么

    2024年02月02日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包