Web 端实时防挡脸弹幕(基于机器学习)

防挡脸弹幕,即大量弹幕飘过,但不会遮挡视频画面中的人物,看起来像是从人物背后飘过去的。

机器学习已经火了好几年了,但很多人都不知道浏览器中也能运行这些能力;

本文介绍在视频弹幕方面的实践优化过程,文末列举了一些本方案可适用的场景,期望能开启一些脑洞。

mediapipe Demo(https://google.github.io/mediapipe/)展示

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

Web 端实时防挡脸弹幕(基于机器学习)

主流防挡脸弹幕实现原理

点播

up 上传视频

服务器后台计算提取视频画面中的人像区域,转换成 svg 存储

客户端播放视频的同时,从服务器下载 svg 与弹幕合成,人像区域不显示弹幕

 直播

主播推流时,实时(主播设备)从画面提取人像区域,转换成 svg将 svg 数据合并到视频流中(SEI),推流至服务器客户端播放视频同时,从视频流中(SEI)解析出 svg将 svg 与弹幕合成,人像区域不显示弹幕

本文实现方案

客户端播放视频同时,实时从画面提取人像区域信息,将人像区域信息导出成图片与弹幕合成,人像区域不显示弹幕。

实现原理

采用机器学习开源库从视频画面实时提取人像轮廓,如Body Segmentation(https://github.com/tensorflow/tfjs-models/blob/master/body-segmentation/README.md)将人像轮廓转导出为图片,设置弹幕层的 mask-image(https://developer.mozilla.org/zh-CN/docs/Web/CSS/mask-image)

 对比传统(直播SEI实时)方案

优点:

易于实现;只需要Video标签一个参数,无需多端协同配合无网络带宽消耗

缺点:

理论性能极限劣于传统方案;相当于性能资源换网络资源

面临的问题

众所周知,JavaScript 的性能较差,因此不适合用于处理 CPU 密集型任务。由官方demo变成工程实践,最大的挑战就是——性能。

本次实践最终将 CPU 占用优化到 5% 左右(2020 M1 Macbook),达到生产可用状态。

实践调优过程

选择机器学习模型

BodyPix (https://github.com/tensorflow/tfjs-models/blob/master/body-segmentation/src/body_pix/README.md)

精确度太差,面部偏窄,有很明显的弹幕与人物面部边缘重叠现象

Web 端实时防挡脸弹幕(基于机器学习)

BlazePose(https://github.com/tensorflow/tfjs-models/blob/master/pose-detection/src/blazepose_mediapipe/README.md)

精确度优秀,且提供了肢体点位信息,但性能较差

Web 端实时防挡脸弹幕(基于机器学习)

返回数据结构示例

[{score: 0.8,keypoints: [{x: 230, y: 220, score: 0.9, score: 0.99, name: "nose"},{x: 212, y: 190, score: 0.8, score: 0.91, name: "left_eye"},...],keypoints3D: [{x: 0.65, y: 0.11, z: 0.05, score: 0.99, name: "nose"},...],segmentation: {maskValueToLabel: (maskValue: number) => { return 'person' },mask: {toCanvasImageSource(): ...toImageData(): ...toTensor(): ...getUnderlyingType(): ...}}}]

MediaPipe SelfieSegmentation (https://github.com/tensorflow/tfjs-models/blob/master/body-segmentation/src/selfie_segmentation_mediapipe/README.md)

精确度优秀(跟 BlazePose 模型效果一致),CPU 占用相对 BlazePose 模型降低 15% 左右,性能取胜,但返回数据中不提供肢体点位信息

返回数据结构示例

{maskValueToLabel: (maskValue: number) => { return 'person' },mask: {toCanvasImageSource(): ...toImageData(): ...toTensor(): ...getUnderlyingType(): ...}}

初版实现

参考 MediaPipe SelfieSegmentation 模型 官方实现(https://github.com/tensorflow/tfjs-models/blob/master/body-segmentation/README.md#bodysegmentationdrawmask),未做优化的情况下 CPU 占用 70% 左右

const canvas = document.createElement('canvas')canvas.width = videoEl.videoWidthcanvas.height = videoEl.videoHeightasync function detect (): Promise {const segmentation = await segmenter.segmentPeople(videoEl)const foregroundColor = { r: 0, g: 0, b: 0, a: 0 }const backgroundColor = { r: 0, g: 0, b: 0, a: 255 } const mask = await toBinaryMask(segmentation, foregroundColor, backgroundColor) await drawMask(canvas, canvas, mask, 1, 9)// 导出Mask图片,需要的是轮廓,图片质量设为最低handler(canvas.toDataURL('image/png', 0)) window.setTimeout(detect, 33)} detect().catch(console.error)

降低提取频率,平衡 性能-体验

一般视频 30FPS,尝试弹幕遮罩(后称 Mask)刷新频率降为 15FPS,体验上还能接受

window.setTimeout(detect, 66) // 33 => 66

此时,CPU 占用 50% 左右

解决性能瓶颈

Web 端实时防挡脸弹幕(基于机器学习)

分析火焰图可发现,性能瓶颈在 toBinaryMask 和 toDataURL

Remove.bg Remove.bg

AI在线抠图软件,图片去除背景

Remove.bg 174 查看详情 Remove.bg

重写toBinaryMask

分析源码,结合打印segmentation的信息,发现segmentation.mask.toCanvasImageSource可获取原始ImageBitmap对象,即是模型提取出来的信息。尝试自己编写代码,将ImageBitmap转换为遮罩(Mask),以代替使用开源库所提供的默认实现。

实现原理

async function detect (): Promise {const segmentation = await segmenter.segmentPeople(videoEl) context.clearRect(0, 0, canvas.width, canvas.height)// 1. 将`ImageBitmap`绘制到 Canvas 上context.drawImage(// 经验证 即使出现多人,也只有一个 segmentationawait segmentation[0].mask.toCanvasImageSource(),0, 0,canvas.width, canvas.height)// 2. 设置混合模式context.globalCompositeOperation = 'source-out'// 3. 反向填充黑色context.fillRect(0, 0, canvas.width, canvas.height)// 导出Mask图片,需要的是轮廓,图片质量设为最低handler(canvas.toDataURL('image/png', 0)) window.setTimeout(detect, 66)}

第 2、3 步相当于给人像区域外的内容填充黑色(反向填充ImageBitmap),是为了配合css(mask-image), 不然只有当弹幕飘到人像区域才可见(与目标效果正好相反)。

globalCompositeOperation MDN(https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation)

此时,CPU 占用 33% 左右

多线程优化

我原先认为toDataURL是由浏览器内部实现的,无法再进行优化,现在只有优化toDataURL这个耗时操作了。

虽没有替换实现,但可使用 OffscreenCanvas (https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas)+ Worker,将耗时任务转移到 Worker 中去, 避免占用主线程,就不会影响用户体验了。

并且ImageBitmap实现了Transferable接口,可被转移所有权,跨 Worker 传递也没有性能损耗(https://hughfenghen.github.io/fe-basic-course/js-concurrent.html#%E4%B8%A4%E4%B8%AA%E6%96%B9%E6%B3%95%E5%AF%B9%E6%AF%94)。

// 前文 detect 的反向填充 ImageBitmap 也可以转移到 Worker 中// 用 OffscreenCanvas 实现, 此处略过 const reader = new FileReaderSync()// OffscreenCanvas 不支持 toDataURL,使用 convertToBlob 代替offsecreenCvsEl.convertToBlob({type: 'image/png',quality: 0}).then((blob) => {const dataURL = reader.readAsDataURL(blob)self.postMessage({msgType: 'mask',val: dataURL})}).catch(console.error)

Web 端实时防挡脸弹幕(基于机器学习)

可以看到两个耗时的操作消失了

此时,CPU 占用 15% 左右

降低分辨率

继续分析,上图重新计算样式(紫色部分)耗时约 3ms

Demo 足够简单很容易推测到是这行代码导致的,发现 imgStr 大概 100kb 左右(视频分辨率 1280×720)。

danmakuContainer.style.webkitMaskImage = `url(${imgStr})

通过canvas缩小图片尺寸(360P甚至更低),再进行推理。

优化后,导出的 imgStr 大概 12kb,重新计算样式耗时约 0.5ms。

此时,CPU 占用 5% 左右

Web 端实时防挡脸弹幕(基于机器学习)

启动条件优化

虽然提取 Mask 整个过程的 CPU 占用已优化到可喜程度。

当在画面没人的时候,或没有弹幕时候,可以停止计算,实现 0 CPU 占用。

无弹幕判断比较简单(比如 10s 内收超过两条弹幕则启动计算),也不在该 SDK 实现范围,略过

 判定画面是否有人

第一步中为了高性能,选择的模型只有ImageBitmap,并没有提供肢体点位信息,所以只能使用getImageData返回的像素点值来判断画面是否有人。

画面无人时,CPU 占用接近 0%

发布构建优化

依赖包的提交较大,构建出的 bundle 体积:684.75 KiB / gzip: 125.83 KiB

所以,可以进行异步加载SDK,提升页面加载性能。

分别打包一个 loader,一个主体由业务方 import loader,首次启用时异步加载主体

这个两步前端工程已经非常成熟了,略过细节。

运行效果

Web 端实时防挡脸弹幕(基于机器学习)

总结

过程

选择高性能模型后,初始状态 CPU 70%降低 Mask 刷新频率(15FPS),CPU 50%重写开源库实现(toBinaryMask),CPU 33%多线程优化,CPU 15%降低分辨率,CPU 5%判断画面是否有人,无人时 CPU 接近 0%

CPU 数值指主线程占用

注意事项

兼容性:Chrome 79及以上,不支持 Firefox、Safari。因为使用了OffscreenCanvas不应创建多个或多次创建segmenter实例(bodySegmentation.createSegmenter),如需复用请保存实例引用,因为:创建实例时低性能设备会有明显的卡顿现象会内存泄露;如果无法避免,这是mediapipe 内存泄露 解决方法(https://github.com/google/mediapipe/issues/2819#issuecomment-1160335349)

经验

优化完成之后,提取并应用 Mask 关键计算量在 GPU (30%左右),而不是 CPU性能优化需要业务场景分析,防挡弹幕场景可以使用低分辨率、低刷新率的 mask-image,能大幅减少计算量该方案其他应用场景:替换/模糊人物背景人像马赛克人像抠图卡通头套,虚拟饰品,如猫耳朵、兔耳朵、带花、戴眼镜什么的(换一个模型,略改)关注Web 神经网络 API (https://mp.weixin.qq.com/s/v7-xwYJqOfFDIAvwIVZVdg)进展,以后实现相关功能也许会更简单

本期作者

Web 端实时防挡脸弹幕(基于机器学习)

                                                                               刘俊   

哔哩哔哩资深开发工程师

以上就是Web 端实时防挡脸弹幕(基于机器学习)的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/975482.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
电脑音频故障排查与解决方案
上一篇 2025年12月1日 20:20:55
Java学习入门指南
下一篇 2025年12月1日 20:20:59

相关推荐

  • 修复Django电商项目中AJAX过滤产品列表图片不显示问题

    在Django电商项目中,当使用AJAX动态加载过滤后的产品列表时,常遇到图片无法正常显示的问题。这通常是由于前端模板中图片加载方式(如data-setbg属性结合JavaScript库)与AJAX动态内容更新机制不兼容所致。解决方案是直接在AJAX返回的HTML中使用标准的标签来渲染图片,确保浏览…

    2026年5月10日
    000
  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    000
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • vscode上怎么运行html_vscode上运行html步骤【指南】

    首先保存文件为.html格式,再通过浏览器或Live Server插件打开预览;推荐安装Live Server实现本地服务器运行与实时刷新,提升开发体验。 在 VS Code 上运行 HTML 文件并不需要复杂的配置,只需几个简单步骤即可预览页面效果。VS Code 本身是一个代码编辑器,不直接运行…

    2026年5月10日
    100
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    000
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    000
  • 前端缓存策略与JavaScript存储管理

    根据数据特性选择合适的存储方式并制定清晰的读写与清理逻辑,能显著提升前端性能;合理运用Cookie、localStorage、sessionStorage、IndexedDB及Cache API,结合缓存策略与定期清理机制,可在保证用户体验的同时避免安全与性能隐患。 前端缓存和JavaScript存…

    2026年5月10日
    100
  • HTML5网页如何实现手势操作 HTML5网页移动端交互的处理技巧

    首先利用原生touch事件实现滑动判断,再通过preventDefault解决滚动冲突,接着引入Hammer.js处理复杂手势,最后通过优化点击区域、避免事件冲突和增加视觉反馈提升体验。 在移动端浏览器中,HTML5网页可以通过触摸事件实现手势操作,提升用户体验。虽然原生JavaScript提供了基…

    2026年5月10日
    000
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • JavaScript 动态菜单点击高亮效果实现教程

    本教程详细介绍了如何使用 JavaScript 实现动态菜单的点击高亮功能。通过事件委托和状态管理,当用户点击菜单项时,被点击项会高亮显示(绿色),同时其他菜单项恢复默认样式(白色)。这种方法避免了不必要的DOM操作,提高了性能和代码可维护性,确保了无论点击方向如何,功能都能稳定运行。 动态菜单高亮…

    2026年5月10日
    200
  • 谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    使用谷歌浏览器的开发者工具截图步骤:1. 按ctrl+shift+i(windows/linux)或cmd+option+i(mac)打开开发者工具。2. 点击右上角三个点,选择”更多工具”,再选择”截图”。3. 选择截取整个页面。推荐的谷歌浏览器扩展…

    2026年5月10日 用户投稿
    100
  • JavaScript函数中插入加载动画(Spinner)的正确方法

    本文旨在解决在JavaScript函数中插入加载动画(Spinner)时遇到的异步问题。通过引入async/await和Promise.all,确保在数据处理完成前后正确显示和隐藏加载动画,提升用户体验。我们将提供两种实现方案,并详细解释其原理和优势。 在Web开发中,当执行耗时操作时,显示加载动画…

    2026年5月10日
    000
  • 动态更新圆形进度条:JavaScript成绩计算器集成指南

    本文档旨在指导开发者如何将JavaScript成绩计算系统与动态圆形进度条集成,实现可视化展示平均成绩。我们将详细讲解如何修改现有的JavaScript代码,使其在计算出平均分后,能够动态更新圆形进度条的进度,从而提供更直观的用户体验。本文档包含详细的代码示例和注意事项,帮助开发者轻松实现这一功能。…

    2026年5月10日
    000
  • CSS伪元素与固定背景:移动友好的实现策略

    本文深入探讨了如何利用CSS的::before伪元素、position: fixed和z-index属性,创建一种在移动设备上表现更稳定的全屏固定背景效果,以替代传统background-attachment: fixed可能存在的兼容性问题。教程将详细解析这些核心CSS概念及其在构建响应式布局中的…

    2026年5月10日
    000
  • 使用 Ajax 和 FormData 实现文件上传及文本数据提交的完整教程

    本文旨在解决在使用 Ajax 和 FormData 进行文件上传时,遇到的 $_POST 和 $_FILES 为空的问题。通过详细的代码示例和解释,我们将展示如何正确地构建 FormData 对象,并通过 Ajax 将文件和文本数据发送到服务器端,同时避免常见的错误配置,确保数据能够成功地被 PHP…

    2026年5月10日
    000
  • JavaScript 高效判断页面所有复选框状态的技巧与实践

    本文旨在提供一套高效且专业的javascript方法,用于判断网页中所有复选框的选中状态。我们将探讨如何利用`array.some()`快速确定是否有未选中的复选框(进而判断是否全部选中),以及如何使用`array.filter()`统计选中和未选中的复选框数量。通过优化dom元素选择和数组操作,提…

    2026年5月10日
    000
  • NextAuth getToken 在服务端返回 null 的问题排查与解决

    问题描述 在使用 Next.js 和 NextAuth 构建应用程序时,有时需要在服务端获取用户的身份验证信息。getToken 函数是 NextAuth 提供的一个便捷方法,用于从请求中提取 JWT (JSON Web Token)。然而,在某些情况下,尤其是在使用 getServerSidePr…

    2026年5月10日
    000
  • HTML表单如何实现PWA支持?怎样添加离线功能?

    答案是利用Service Worker缓存资源并结合Background Sync API实现离线提交与自动同步。通过注册Service Worker缓存表单相关文件,拦截提交行为,将离线数据存入IndexedDB,并注册后台同步任务,待网络恢复后由Service Worker自动发送数据,确保提交…

    2026年5月10日
    000
  • 虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版

    虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版

    虫虫漫画官网入口为www.ccmh.com,用户可直接通过浏览器访问,支持多端适配与账号同步功能,界面简洁无广告,提供海量国漫、日漫、韩漫资源,涵盖恋爱、玄幻等热门题材,更新及时,支持多种阅读模式及离线缓存,阅读体验流畅。 虫虫漫画直接进入官网入口在哪里?这是不少网友都关注的,接下来由PHP小编为大…

    2026年5月10日 用户投稿
    000
  • CSS技巧:在复杂悬停效果中确保图像始终可见

    CSS技巧:在复杂悬停效果中确保图像始终可见CSS技巧:在复杂悬停效果中确保图像始终可见CSS技巧:在复杂悬停效果中确保图像始终可见CSS技巧:在复杂悬停效果中确保图像始终可见

    本教程探讨如何在包含悬停效果的CSS卡片布局中,确保图像始终显示在最顶层而不被裁剪或遮挡。通过调整HTML结构,利用CSS的position和z-index属性,以及引入pointer-events,我们将解决图像被overflow: hidden和扩展叠加层遮盖的问题,实现复杂的视觉交互效果。 在…

    2026年5月10日 用户投稿
    000

发表回复

登录后才能评论
关注微信