【问题标题】:Use Alpha-Channeled video to mask HTML content使用 Alpha-Channeled 视频屏蔽 HTML 内容
【发布时间】:2015-03-27 18:25:05
【问题描述】:

我正在尝试实现当前可能在后效中实现的效果,该效果是关于一个视频与另一个视频的剪辑遮罩(亮度遮罩),因此最终输出将是一个透明的视频。但我正在尝试制作动态透明的 HTML 内容。

视频看起来像这样:https://www.youtube.com/watch?v=jfzcrO4694E

基本上以不断变化的透明度显示 HTML 内容。

这必须是关于 + 的东西,但是在谷歌搜索大约一个小时后,找不到任何可以描述和影响会改变 HTML 内容透明度的黑白视频的内容。

有什么想法吗?

【问题讨论】:

标签: html css canvas html5-video


【解决方案1】:

HTML5 不支持 alpha 通道视频(例如 MOV 32 位等)。

唯一的选择是使用哑光合成,就像您的视频结合画布元素一样。但是,canvas 只能合成已经存在的 alpha 通道,并且由于 matte 是一个纯层而不是 alpha,所以您必须将其转换为 alpha。

这意味着您必须在画布上绘制和迭代每一帧,并将灰度转换为透明度级别。但是,这会占用大量 CPU 资源,并且会影响计算机性能/资源。视频还必须满足 CORS 要求才能不污染画布(在这种情况下,您无法提取像素)。

方法如下

对于每一帧:

  • 从任何通道为每个像素获取一个级别,我们将在这里使用红色
  • 从 255 中减去该值,因为此处的哑光白色将成为目标(反转,将显示)
  • 清除 图像 数据,这是遮罩本身,否则您会得到白色遮罩泄漏到数据中
  • 将计算值设置为新的 Alpha 通道值

示例循环(假设视频是动态创建的,已设置其源并已触发启动事件 - 画布放置在您要屏蔽的位置并初始化上下文 ctx):

ctx.globalCompositeOperation = "copy";       // will clear previous alpha

function unmask() {
    ctx.drawImage(video, 0, 0);              // draw current video frame

    var idata = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height),
        data = idata.data,
        len = data.length, i = 0;            // ..image data from canvas

    while(i < len) {
        var alpha = 255 - data[i];           // in this matte, white = fully transparent
        data[i] = data[i+1] = data[i+2] = 0; // clear matte to black
        data[i+3] = alpha;                   // set alpha
        i += 4;                              // next pixel
    }

    ctx.putImageData(idata, 0, 0);           // update canvas
    requestAnimationFrame(unmask);           // todo: add criteria to stop this
}

加速技巧:使用黑色(或预先反转哑光视频),然后对数据使用 Uint32Array 视图,将像素长字 (


使用新的CSS custom filters(又名 CSS 着色器)有可能实现此目的,但在撰写本文时尚未得到广泛支持。它还需要使用着色器语言 (GLSL)。但我觉得至少值得一提。

【讨论】:

  • > "canvas 放置在您要屏蔽的位置,上下文 ctx 已初始化" - 所以如果我想屏蔽整个页面,我将所有内容放入 标记中,对吗?跨度>
  • @DavidS。正确,只要记住设置它的大小。在这种情况下,您可能希望将其设置为视频大小并使用 CSS 来填充其余部分。
  • 谢谢,我会试试看!如果我让它工作,我会在这里发布。
  • 显然 Chrome 和 Opera 确实支持视频中的 alpha 通道,正如 this newer question 中所指出的那样。
  • 此外,CSS 自定义过滤器似乎未能获得认可。该实现已从 Chrome 和 Webkit over a year ago 中删除。但是请参阅我对这个问题的(不是很完整的)回答,了解在当今的浏览器中为此目的使用 GLSL 的方法。
【解决方案2】:

到目前为止,其他答案都假设为 2D 画布上下文。您可以使用 WebGL 更有效地完成此操作。这样,所有实际工作都在 GPU 中完成。

您需要一个片段着色器,它基本上可以完成 Ken Fyrstenberg 的回答中循环所做的工作:找到每个像素的亮度,从最大值中减去它(在 GLSL 中为 1,而不是 255),然后将其用作 alpha价值。我将按照他的做法 (a) 仅使用绿色通道作为亮度,以及 (b) 将输出颜色通道设置为黑色。这是我的做法:

precision lowp float;
uniform sampler2D sampler0;
varying vec2 v_texCoord;
void main(void) {
  vec4 color = texture2D(sampler0, v_texCoord);
  float alpha = 1.0 - color.g;
  gl_FragColor = vec4(0.0, 0.0, 0.0, alpha);
}

然后,对于每一帧,您将一个矩形渲染到 WebGL 画布,您的视频作为纹理源,上面的片段着色器。 我已经开始编写如何执行此操作的示例,但这需要一些工作。有多少利息?让我知道,如果有足够的需求,我会尽力完成它。

编辑:您可以通过使用glfx.js 避免过多地了解 WebGL。我没有深入研究它,但我认为它可以使用视频作为图像源。

【讨论】:

    【解决方案3】:

    示例视频的白色区域越来越多地被后退的黑色区域显露出来。

    您可以使用现有的视频 + 画布来显示底层 html 内容。

    创建一个包含您已经制作的视频的视频元素。

    使用计时器来抓取正在播放的视频的帧。强烈建议使用requestAnimationFrame,因为它会与您的显示刷新同步。

    在每个动画循环中,在画布上绘制视频帧。您可以在画布上绘制视频,因为画布可以使用视频元素作为其图像源。

    一旦你的黑雾框架出现在画布上,你可以通过以下方式使画布的任何白色区域透明:

    • 使用context.getImageData获取画布上每个像素的颜色数据

    • 如果任何像素的颜色为“白色”,您可以将该像素的 alpha 更改为 0(表示透明)。

    • 使用context.putImageData将修改后的像素数据放回画布上

    总的来说:

    • 隐藏正在播放的视频元素

    • 使用 CSS 将您的画布元素放置在您希望显示的 html 内容上。

    • 创建一个动画循环,在画布上绘制视频帧并将白色像素转换为透明。

    • html 内容将通过画布播放视频的帧来显示,其中白色像素被敲成完全透明。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-03
      • 1970-01-01
      • 2018-09-16
      • 2014-09-04
      • 2018-10-01
      • 2014-09-20
      • 1970-01-01
      • 2015-02-13
      相关资源
      最近更新 更多