欢迎光临散文网 会员登陆 & 注册

随便扣个GB/BB视频。(JS实现,内含代码适度食用)

2020-07-02 20:30 作者:暗切线  | 我要投稿

最近动态里一大堆的GB/BB的视频。觉得挺有意思。JS扣一下康康??

鲁迅同志曾说过,得把引用写上:源文件是这个的 1 和 22 P

声明一下:我知道webgl做效率高很多。但是由于webgl代码我写了你们也不一定能看懂。所以只贴一些原理性的东西。。

好吧先看结果:(直接截图了)

22
1


  1. 准备必要的东西

    为了通用性更强一点,所以先做了视频背景颜色的分析:

    渲染用的幕布

    let canvas = document.querySelector('canvas')

    let ctx = canvas.getContext('2d')

    不说啥了。这很好理解,毕竟得展示出来。


    离屏幕布

    let offscreenCanvas  = document.createElement('canvas');

    let offCtx = offscreenCanvas.getContext('2d')

    分析和色彩计算,如果渲染出来效率会非常低。众所周知,JS的渲染进程和执行进程相互阻塞。所以需要一个离屏的幕布。


    分析时候用的一些变量

    const colorcount = {}  // 记录色彩

    let analyseCount = 0;

    const LIMIT_ANALYSE = 10 // 最多分析的帧数。

    const Y_RANGE = 180; // 允许的色彩偏差范围值

    const UV_RANGE = 80;// 允许的色彩偏差范围值

    这里需要注意几点

    1)colorcount的声明,我用的对象。为啥不是数组?[{color: count}]的模式,熟悉JS的小伙伴应该知道 如果声明成数组。对于之后的排序会友好很多。但是,数组set的时候你需要做二叉查找。而对象,这样写会变成hash查找。对于排序我们就在分析结束的时候做一次。而设置颜色的Count。每一帧每一个像素都要做。所以是对象。

      视频当然也得是离屏

      let video = document.createElement('video')

      video.src = './gb.mp4'

      video.muted = true; 

        

      幕布颜色值。做的比较简单。(不要学我瞎写)

      let yuvcolor = undefined


2. 视频分析

if(analyseCount < LIMIT_ANALYSE) {

  记录色彩(见后面)

  analyseCount++

  requestAnimationFrame(analyse)

} else {

    分析(见后面)

}

记录色彩部分

离屏绘制

offCtx.drawImage(video, 0,0, offscreenCanvas.width, offscreenCanvas.height, 0,0, video.videoWidth, video.videoHeight)

不多说,把像素画到离屏的幕布上

var idata = offCtx.getImageData(0,0, offscreenCanvas.width, offscreenCanvas.height)

取到像素值

if (idata && idata.data) { // 这是一个好习惯!!

    for(let i=0;i<idata.data.length;i+=4) {

        // 每个像素4位

        if(idata.data[i + 3]!= 0) {

        // 制造key

        let colorindex = idata.data[i] + ',' + idata.data[i+1] + ',' + idata.data[i+2]

        // 不存在时候,创建色彩,并计数1

        if (!colorcount[colorindex]) colorcount[colorindex] = 1;

        // 存在的时候更新数目

        else colorcount[colorindex] ++;

    }

}

特殊说明:如果是webgl做分析。这里可以搞texture来存储。效率高很多。


分析颜色

let maxcolor = '0,0,0'

let maxcount = 0

for(i in colorcount) { // 非常差劲!!!!for(xxx;xxx;xxx)的写法会更好!

    if(colorcount[i] > maxcount) {

        maxcolor = i

        maxcount = colorcount[i]

    }

}

// 分析色彩和转换YUV

let color = [parseInt(maxcolor.split(",")[0]), parseInt(maxcolor.split(",")[1]), parseInt(maxcolor.split(",")[2])]

let y = 0.299 * color[0] + 0.587 * color[1] + 0.114 * color[2]

let u =-0.169 * color[0] - 0.331 * color[1] + 0.500 * color[2]

let v= 0.500 * color[0] - 0.439 * color[1] - 0.081 * color[2]

yuvcolor = [y,u,v]


首先 这段代码写得非常差。原因如下

        1. 我图省事写了 for(i in xxx)众所周知。这个效率非常低

        2. 其实不用YUV也可以。这样的话,去掉绿幕的时候每一帧都得转。另外,YUV转换可以抽出来成方法的。。所以非常差劲


3. 去掉绿幕:

还是先在离屏幕布画图!

offCtx.drawImage(video, 0,0, video.videoWidth, video.videoHeight, 0,0, video.videoWidth, video.videoHeight)

还是取画面

var idata = offCtx.getImageData(0,0, video.videoWidth, video.videoHeight)


for (let i=0; i<idata.data.length; i+=4) {

    // YUV转换

    let y = 0.299 * idata.data[i] + 0.587 * idata.data[i+1] + 0.114 *idata.data[i+2]

    let u =-0.169 * idata.data[i] - 0.331 * idata.data[i+1] + 0.500 * idata.data[i+2]

    let v= 0.500 * idata.data[i] - 0.439 * idata.data[i+1] - 0.081 * idata.data[i+2]

    // 很好理解吧。范围内的值直接把透明度设置为0

    if(y < yuvcolor[0] + Y_RANGE

        && y > yuvcolor[0] - Y_RANGE

        && u < yuvcolor[1] + UV_RANGE

        && u > yuvcolor[1] - UV_RANGE

        && v < yuvcolor[2] + UV_RANGE

        && v > yuvcolor[2] - UV_RANGE

        ) {

        idata.data[i+3] = 0;

    }

}

// 画图

ctx.putImageData(idata, 0,0)

requestAnimationFrame(draw)


4. 处理时机:

analyse 在 canplay的时候就行了。。。

播放的时候处理去绿幕


5. 一些说明

老生长谈:视频压缩会有数据损失。所以不会是那个颜色值。加了个RANGE。请叫我调参工程师。。。


6. 代码烂在哪?

暗切线同学啊,,谁™教你写全局变量的??


就说这么多吧。这篇文章非常水。。。感觉完全是灌水的。。。你们随意喷吧。。。

交流群:711929228

本期问题:yuv420p指的是啥?

答案:像素格式。


随便扣个GB/BB视频。(JS实现,内含代码适度食用)的评论 (共 条)

分享到微博请遵守国家法律