const canvas = document.createElement('canvas')canvas.width = videoEl.videoWidthcanvas.height = videoEl.videoHeightasync function detect (): Promise<void> {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)
ウェブ上の顔面ブロック攻撃に対するリアルタイム保護 (機械学習に基づく)
対顔弾幕、つまりビデオ画面内のキャラクターを遮らずに大量の弾幕が飛び交い、キャラクターの背後から浮遊しているように見えます。
機械学習は数年前から普及していますが、多くの人はこれらの機能がブラウザでも実行できることを知りません;
この記事では、ビデオの集中砲火における実践的な最適化プロセスを紹介します。記事の最後に、このソリューションが適用できるいくつかのシナリオについて説明し、いくつかのアイデアが生まれることを期待しています。
mediapipe デモ(https://google.github.io/mediapipe/)は、
主流の対顔弾幕実装原理を示します
オンデマンド
up ビデオのアップロード
サーバーのバックグラウンド計算により、ビデオ画面のポートレート領域が抽出され、SVG ストレージに変換されます
クライアントがビデオを再生している間、サーバーからsvgをダウンロードします 弾幕と組み合わせると、ポートレートエリアには弾幕が表示されません
ライブブロードキャスト
- アンカーがストリームをプッシュすると、ポートレートエリアがストリームから抽出されますリアルタイムで画面 (ホスト デバイス) を表示し、svg に変換します
- svg データをビデオ ストリーム (SEI) にマージし、ストリームをサーバーにプッシュします #クライアントが再生すると同時に、ビデオ、ビデオ ストリーム (SEI) から SVG を解析します。
- 箇条書きで SVG をコンパイルします。画面合成、ポートレート領域には弾幕が表示されません。
- 機械学習オープンソース ライブラリを使用して、ボディ セグメンテーション (https://github.com/tensorflow/tfjs) など、ビデオ画像からポートレートの輪郭をリアルタイムで抽出します。 -models/blob/master/body-segmentation/README.md)
- ポートレートのアウトラインを画像にエクスポートし、弾幕レイヤーのマスク画像を設定します (https://developer.mozilla.org/) zh-CN/docs/ Web/CSS/mask-image)
- 実装が簡単; 必要なビデオタグは 1 つだけ パラメータ、マルチエンド調整の必要なし
- #ネットワーク帯域幅の消費なし
- #欠点:
理論上のパフォーマンス限界は従来のソリューションよりも劣っており、ネットワーク リソースのパフォーマンス リソースを交換するのと同等です
- 直面する問題
BlazePose (https://github.com/tensorflow/tfjs-models/blob/master/pose-detection/src/blazepose_mediapipe/README.md)
戻りデータ構造の例
[{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(): ...}}}]
{maskValueToLabel: (maskValue: number) => { return 'person' },mask: {toCanvasImageSource(): ...toImageData(): ...toTensor(): ...getUnderlyingType(): ...}}
const canvas = document.createElement('canvas')canvas.width = videoEl.videoWidthcanvas.height = videoEl.videoHeightasync function detect (): Promise<void> {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 ですが、弾幕マスク (以下、マスクと呼びます) の更新頻度を 15FPS に下げてみてください。これはエクスペリエンスの観点からはまだ許容範囲です。
const canvas = document.createElement('canvas')canvas.width = videoEl.videoWidthcanvas.height = videoEl.videoHeightasync function detect (): Promise<void> {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)
window.setTimeout(detect, 66) // 33 => 66
フレーム グラフを分析すると、パフォーマンスのボトルネックが次の場所にあることがわかります。 toBinaryMask と toDataURL
async function detect (): Promise<void> {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)}
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)
可以看到两个耗时的操作消失了
此时,CPU 占用 15% 左右
降低分辨率
继续分析,上图重新计算样式(紫色部分)耗时约 3ms
Demo 足够简单很容易推测到是这行代码导致的,发现 imgStr 大概 100kb 左右(视频分辨率 1280x720)。
danmakuContainer.style.webkitMaskImage = `url(${imgStr})
通过canvas缩小图片尺寸(360P甚至更低),再进行推理。
优化后,导出的 imgStr 大概 12kb,重新计算样式耗时约 0.5ms。
此时,CPU 占用 5% 左右
启动条件优化
虽然提取 Mask 整个过程的 CPU 占用已优化到可喜程度。
当在画面没人的时候,或没有弹幕时候,可以停止计算,实现 0 CPU 占用。
无弹幕判断比较简单(比如 10s 内收超过两条弹幕则启动计算),也不在该 SDK 实现范围,略过
判定画面是否有人
第一步中为了高性能,选择的模型只有ImageBitmap,并没有提供肢体点位信息,所以只能使用getImageData返回的像素点值来判断画面是否有人。
画面无人时,CPU 占用接近 0%
发布构建优化
依赖包的提交较大,构建出的 bundle 体积:684.75 KiB / gzip: 125.83 KiB
所以,可以进行异步加载SDK,提升页面加载性能。
- 分别打包一个 loader,一个主体
- 由业务方 import loader,首次启用时异步加载主体
这个两步前端工程已经非常成熟了,略过细节。
运行效果
总结
过程
- 选择高性能模型后,初始状态 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)进展,以后实现相关功能也许会更简单
本期作者
刘俊
Bilibili シニア開発エンジニア
以上がウェブ上の顔面ブロック攻撃に対するリアルタイム保護 (機械学習に基づく)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









CSSでサイズ変更シンボルをカスタマイズする方法は、背景色で統一されています。毎日の開発では、調整など、ユーザーインターフェイスの詳細をカスタマイズする必要がある状況に遭遇することがよくあります...

最近、Webページにローカルにインストールされたフォントファイルを使用して、インターネットから無料のフォントをダウンロードし、システムに正常にインストールしました。今...

フレックスレイアウトとソリューションの下でのテキストの過度の省略によるコンテナの開口部の問題が使用されます...

ユーザーエージェントスタイルシートによって引き起こされるディスプレイの問題を解決する方法は? Edgeブラウザを使用する場合、プロジェクトのDIV要素を表示することはできません。チェックした後、私は投稿しました...

JavaScriptまたはCSSを使用して、ブラウザの印刷設定のページの上部と端を制御する方法。ブラウザの印刷設定には、ディスプレイが...

なぜマイナスマージンが場合によっては有効にならないのですか?プログラミング中、CSSの負のマージン(負...

Webページにローカルにインストールされたフォントファイルを使用する方法Webページ開発でこの状況に遭遇しましたか:コンピューターにフォントをインストールしました...

Safariでカスタムスタイルシートを使用することについてのディスカッション今日は、Safariブラウザ向けのカスタムスタイルシートのアプリケーションに関する質問について説明します。フロントエンド初心者...
