偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Web 端實(shí)時(shí)防擋臉彈幕(基于機(jī)器學(xué)習(xí))

人工智能 機(jī)器學(xué)習(xí)
客戶(hù)端播放視頻同時(shí),實(shí)時(shí)從畫(huà)面提取人像區(qū)域信息,將人像區(qū)域信息導(dǎo)出成圖片與彈幕合成,人像區(qū)域不顯示彈幕。

防擋臉彈幕,即大量彈幕飄過(guò),但不會(huì)遮擋視頻畫(huà)面中的人物,看起來(lái)像是從人物背后飄過(guò)去的。

機(jī)器學(xué)習(xí)已經(jīng)火了好幾年了,但很多人都不知道瀏覽器中也能運(yùn)行這些能力;

本文介紹在視頻彈幕方面的實(shí)踐優(yōu)化過(guò)程,文末列舉了一些本方案可適用的場(chǎng)景,期望能開(kāi)啟一些腦洞。

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

主流防擋臉彈幕實(shí)現(xiàn)原理

點(diǎn)播

up 上傳視頻

服務(wù)器后臺(tái)計(jì)算提取視頻畫(huà)面中的人像區(qū)域,轉(zhuǎn)換成 svg 存儲(chǔ)

客戶(hù)端播放視頻的同時(shí),從服務(wù)器下載 svg 與彈幕合成,人像區(qū)域不顯示彈幕

 直播

  1. 主播推流時(shí),實(shí)時(shí)(主播設(shè)備)從畫(huà)面提取人像區(qū)域,轉(zhuǎn)換成 svg
  2. 將 svg 數(shù)據(jù)合并到視頻流中(SEI),推流至服務(wù)器
  3. 客戶(hù)端播放視頻同時(shí),從視頻流中(SEI)解析出 svg
  4. 將 svg 與彈幕合成,人像區(qū)域不顯示彈幕

本文實(shí)現(xiàn)方案

客戶(hù)端播放視頻同時(shí),實(shí)時(shí)從畫(huà)面提取人像區(qū)域信息,將人像區(qū)域信息導(dǎo)出成圖片與彈幕合成,人像區(qū)域不顯示彈幕。

實(shí)現(xiàn)原理

  1. 采用機(jī)器學(xué)習(xí)開(kāi)源庫(kù)從視頻畫(huà)面實(shí)時(shí)提取人像輪廓,如Body Segmentation(https://github.com/tensorflow/tfjs-models/blob/master/body-segmentation/README.md)
  2. 將人像輪廓轉(zhuǎn)導(dǎo)出為圖片,設(shè)置彈幕層的 mask-image(https://developer.mozilla.org/zh-CN/docs/Web/CSS/mask-image)

 對(duì)比傳統(tǒng)(直播SEI實(shí)時(shí))方案

優(yōu)點(diǎn):

  • 易于實(shí)現(xiàn);只需要Video標(biāo)簽一個(gè)參數(shù),無(wú)需多端協(xié)同配合
  • 無(wú)網(wǎng)絡(luò)帶寬消耗

缺點(diǎn):

  • 理論性能極限劣于傳統(tǒng)方案;相當(dāng)于性能資源換網(wǎng)絡(luò)資源

面臨的問(wèn)題

眾所周知“JS 性能太辣雞”,不適合執(zhí)行 CPU 密集型任務(wù)。由官方demo變成工程實(shí)踐,最大的挑戰(zhàn)就是——性能。

本次實(shí)踐最終將 CPU 占用優(yōu)化到 5% 左右(2020 M1 Macbook),達(dá)到生產(chǎn)可用狀態(tài)。

實(shí)踐調(diào)優(yōu)過(guò)程

選擇機(jī)器學(xué)習(xí)模型

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

精確度太差,面部偏窄,有很明顯的彈幕與人物面部邊緣重疊現(xiàn)象

圖片

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

精確度優(yōu)秀,且提供了肢體點(diǎn)位信息,但性能較差

圖片

返回?cái)?shù)據(jù)結(jié)構(gòu)示例

[
  {
    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)

精確度優(yōu)秀(跟 BlazePose 模型效果一致),CPU 占用相對(duì) BlazePose 模型降低 15% 左右,性能取勝,但返回?cái)?shù)據(jù)中不提供肢體點(diǎn)位信息

返回?cái)?shù)據(jù)結(jié)構(gòu)示例

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

初版實(shí)現(xiàn)

參考 MediaPipe SelfieSegmentation 模型 官方實(shí)現(xiàn)(https://github.com/tensorflow/tfjs-models/blob/master/body-segmentation/README.md#bodysegmentationdrawmask),未做優(yōu)化的情況下 CPU 占用 70% 左右

const canvas = document.createElement('canvas')
canvas.width = videoEl.videoWidth
canvas.height = videoEl.videoHeight
async 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)
  // 導(dǎo)出Mask圖片,需要的是輪廓,圖片質(zhì)量設(shè)為最低
  handler(canvas.toDataURL('image/png', 0))
 
  window.setTimeout(detect, 33)
}
 
detect().catch(console.error)

降低提取頻率,平衡 性能-體驗(yàn)

一般視頻 30FPS,嘗試彈幕遮罩(后稱(chēng) Mask)刷新頻率降為 15FPS,體驗(yàn)上還能接受

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

此時(shí),CPU 占用 50% 左右

解決性能瓶頸

圖片

分析火焰圖可發(fā)現(xiàn),性能瓶頸在 toBinaryMask 和 toDataURL

重寫(xiě)toBinaryMask

分析源碼,結(jié)合打印segmentation的信息,發(fā)現(xiàn)segmentation.mask.toCanvasImageSource可獲取原始ImageBitmap對(duì)象,即是模型提取出來(lái)的信息。嘗試自行實(shí)現(xiàn)將ImageBitmap轉(zhuǎn)換成 Mask 的能力,替換開(kāi)源庫(kù)提供的默認(rèn)實(shí)現(xiàn)。

實(shí)現(xiàn)原理

async function detect (): Promise<void> {
  const segmentation = await segmenter.segmentPeople(videoEl)
 
  context.clearRect(0, 0, canvas.width, canvas.height)
  // 1. 將`ImageBitmap`繪制到 Canvas 上
  context.drawImage(
    // 經(jīng)驗(yàn)證 即使出現(xiàn)多人,也只有一個(gè) segmentation
    await segmentation[0].mask.toCanvasImageSource(),
    0, 0,
    canvas.width, canvas.height
  )
  // 2. 設(shè)置混合模式
  context.globalCompositeOperation = 'source-out'
  // 3. 反向填充黑色
  context.fillRect(0, 0, canvas.width, canvas.height)
  // 導(dǎo)出Mask圖片,需要的是輪廓,圖片質(zhì)量設(shè)為最低
  handler(canvas.toDataURL('image/png', 0))
 
  window.setTimeout(detect, 66)
}

第 2、3 步相當(dāng)于給人像區(qū)域外的內(nèi)容填充黑色(反向填充ImageBitmap),是為了配合css(mask-image), 不然只有當(dāng)彈幕飄到人像區(qū)域才可見(jiàn)(與目標(biāo)效果正好相反)。

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

此時(shí),CPU 占用 33% 左右

多線(xiàn)程優(yōu)化

只剩下toDataURL這個(gè)耗時(shí)操作了,本以為toDataURL是瀏覽器內(nèi)部實(shí)現(xiàn),無(wú)法再進(jìn)行優(yōu)化了。

雖沒(méi)有替換實(shí)現(xiàn),但可使用 OffscreenCanvas (https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas)+ Worker,將耗時(shí)任務(wù)轉(zhuǎn)移到 Worker 中去, 避免占用主線(xiàn)程,就不會(huì)影響用戶(hù)體驗(yàn)了。

并且ImageBitmap實(shí)現(xiàn)了Transferable接口,可被轉(zhuǎn)移所有權(quán),跨 Worker 傳遞也沒(méi)有性能損耗(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 也可以轉(zhuǎn)移到 Worker 中
// 用 OffscreenCanvas 實(shí)現(xiàn), 此處略過(guò)
 
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)

圖片

可以看到兩個(gè)耗時(shí)的操作消失了

此時(shí),CPU 占用 15% 左右

降低分辨率

繼續(xù)分析,上圖重新計(jì)算樣式(紫色部分)耗時(shí)約 3ms

Demo 足夠簡(jiǎn)單很容易推測(cè)到是這行代碼導(dǎo)致的,發(fā)現(xiàn) imgStr 大概 100kb 左右(視頻分辨率 1280x720)。

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

通過(guò)canvas縮小圖片尺寸(360P甚至更低),再進(jìn)行推理。

優(yōu)化后,導(dǎo)出的 imgStr 大概 12kb,重新計(jì)算樣式耗時(shí)約 0.5ms。

此時(shí),CPU 占用 5% 左右

圖片

啟動(dòng)條件優(yōu)化

雖然提取 Mask 整個(gè)過(guò)程的 CPU 占用已優(yōu)化到可喜程度。

當(dāng)在畫(huà)面沒(méi)人的時(shí)候,或沒(méi)有彈幕時(shí)候,可以停止計(jì)算,實(shí)現(xiàn) 0 CPU 占用。

無(wú)彈幕判斷比較簡(jiǎn)單(比如 10s 內(nèi)收超過(guò)兩條彈幕則啟動(dòng)計(jì)算),也不在該 SDK 實(shí)現(xiàn)范圍,略過(guò)

判定畫(huà)面是否有人

第一步中為了高性能,選擇的模型只有ImageBitmap,并沒(méi)有提供肢體點(diǎn)位信息,所以只能使用getImageData返回的像素點(diǎn)值來(lái)判斷畫(huà)面是否有人。

畫(huà)面無(wú)人時(shí),CPU 占用接近 0%

發(fā)布構(gòu)建優(yōu)化

依賴(lài)包的提交較大,構(gòu)建出的 bundle 體積:684.75 KiB / gzip: 125.83 KiB

所以,可以進(jìn)行異步加載SDK,提升頁(yè)面加載性能。

  1. 分別打包一個(gè) loader,一個(gè)主體
  2. 由業(yè)務(wù)方 import loader,首次啟用時(shí)異步加載主體

這個(gè)兩步前端工程已經(jīng)非常成熟了,略過(guò)細(xì)節(jié)。

運(yùn)行效果

總結(jié)

過(guò)程

  • 選擇高性能模型后,初始狀態(tài) CPU 70%
  • 降低 Mask 刷新頻率(15FPS),CPU 50%
  • 重寫(xiě)開(kāi)源庫(kù)實(shí)現(xiàn)(toBinaryMask),CPU 33%
  • 多線(xiàn)程優(yōu)化,CPU 15%
  • 降低分辨率,CPU 5%
  • 判斷畫(huà)面是否有人,無(wú)人時(shí) CPU 接近 0%

CPU 數(shù)值指主線(xiàn)程占用

注意事項(xiàng)

  • 兼容性:Chrome 79及以上,不支持 Firefox、Safari。因?yàn)槭褂昧薕ffscreenCanvas
  • 不應(yīng)創(chuàng)建多個(gè)或多次創(chuàng)建segmenter實(shí)例(bodySegmentation.createSegmenter),如需復(fù)用請(qǐng)保存實(shí)例引用,因?yàn)椋?/li>
  • 創(chuàng)建實(shí)例時(shí)低性能設(shè)備會(huì)有明顯的卡頓現(xiàn)象
  • 會(huì)內(nèi)存泄露;如果無(wú)法避免,這是mediapipe 內(nèi)存泄露 解決方法(https://github.com/google/mediapipe/issues/2819#issuecomment-1160335349)

經(jīng)驗(yàn)

  • 優(yōu)化完成之后,提取并應(yīng)用 Mask 關(guān)鍵計(jì)算量在 GPU (30%左右),而不是 CPU
  • 性能優(yōu)化需要業(yè)務(wù)場(chǎng)景分析,防擋彈幕場(chǎng)景可以使用低分辨率、低刷新率的 mask-image,能大幅減少計(jì)算量
  • 該方案其他應(yīng)用場(chǎng)景:
  • 替換/模糊人物背景
  • 人像馬賽克
  • 人像摳圖
  • 卡通頭套,虛擬飾品,如貓耳朵、兔耳朵、帶花、戴眼鏡什么的(換一個(gè)模型,略改)
  • 關(guān)注Web 神經(jīng)網(wǎng)絡(luò) API (https://mp.weixin.qq.com/s/v7-xwYJqOfFDIAvwIVZVdg)進(jìn)展,以后實(shí)現(xiàn)相關(guān)功能也許會(huì)更簡(jiǎn)單

本期作者

圖片

  劉俊

嗶哩嗶哩資深開(kāi)發(fā)工程師

責(zé)任編輯:武曉燕 來(lái)源: 嗶哩嗶哩技術(shù)
相關(guān)推薦

2017-02-16 08:25:35

2014-03-25 14:21:18

WebSocket實(shí)時(shí)

2020-08-03 07:59:12

機(jī)器學(xué)習(xí)開(kāi)發(fā)數(shù)據(jù)

2024-11-04 08:14:48

2018-08-30 14:58:12

機(jī)器學(xué)習(xí)磁盤(pán)故障

2022-03-28 09:00:00

SQL數(shù)據(jù)庫(kù)機(jī)器學(xué)習(xí)

2019-06-25 10:09:42

Web攻擊機(jī)器學(xué)習(xí)網(wǎng)絡(luò)攻擊

2016-07-29 13:47:05

RethinkDBWeb

2024-05-17 13:17:39

2022-05-16 12:06:00

機(jī)器學(xué)習(xí)深度學(xué)習(xí)模型

2017-04-08 17:32:39

人工智能喬丹Ray

2023-12-01 10:21:00

機(jī)器學(xué)習(xí)算法

2025-06-16 01:00:00

彈幕系統(tǒng)架構(gòu)

2025-06-16 04:00:00

Spring彈幕技術(shù)

2024-06-06 08:00:00

2023-09-27 07:56:25

2021-01-26 09:46:59

PythonStacking機(jī)器學(xué)習(xí)

2022-04-15 10:52:50

模型技術(shù)實(shí)踐

2020-11-25 08:24:13

人臉識(shí)別

2021-06-15 10:41:00

數(shù)據(jù)中毒機(jī)器學(xué)習(xí)網(wǎng)絡(luò)攻擊
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)