얼굴 가리는 탄막을 방지하세요. 즉, 다수의 탄막이 떠다니지만, 영상 화면 속 사람들은 사람들 뒤에서 떠다니는 것처럼 보입니다.
머신 러닝은 몇 년 동안 인기가 있었지만 많은 사람들은 이러한 기능이 브라우저에서도 실행될 수 있다는 사실을 모릅니다.
이 기사에서는 기사 마지막 부분에서 몇 가지 시나리오를 소개합니다. 이 솔루션을 적용할 수 있는 곳이 나열되어 있으며 몇 가지 아이디어를 얻을 수 있기를 바랍니다.
mediapipe 데모(https://google.github.io/mediapipe/)는
업 업로드 비디오
추출을 위한 서버 백그라운드 계산을 보여줍니다. 비디오 화면의 콘텐츠 세로 영역, svg 저장 공간으로 변환
클라이언트가 비디오를 재생하는 동안 svg가 서버에서 다운로드되어 사격과 결합됩니다. 포격은 세로 영역에 표시되지 않습니다
클라이언트가 비디오를 재생하는 동안 , 화면에서 초상화 영역 정보가 실시간으로 추출되어 초상화 영역 정보를 사진으로 내보내어 포격과 결합하며, 초상화 영역에는 포격이 표시되지 않습니다.
기존(실시간 라이브 방송 SEI) 솔루션과 비교
장점:
단점 :
JavaScript는 성능이 좋지 않아 CPU 집약적인 작업을 처리하는 데 적합하지 않다는 것이 잘 알려져 있습니다. 공식 데모부터 엔지니어링 실습까지 가장 큰 과제는 성능입니다.
이 연습을 통해 마침내 CPU 사용량이 약 5%(2020 M1 Macbook)로 최적화되어 프로덕션 준비 상태에 도달했습니다.
BodyPix (https://github.com/tensorflow/tfjs-models/blob/master/body-segmentation/src/body_pix/README.md)
정확도 안타깝지만 얼굴이 너무 좁고 포격의 가장자리와 캐릭터의 얼굴이 명백히 겹칩니다
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(): ...}}}]
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<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로 줄이면 경험이 좋습니다. 여전히 허용됩니다
window.setTimeout(detect, 66) // 33 => 66
이때 CPU가 약 50%를 차지합니다
성능 병목 현상을 해결하세요
플레임 그래프를 분석해 보면, 성능 병목 현상은 toBinaryMask 및 toDataURL
소스 코드를 분석하고 분할 정보 인쇄와 결합하여 세그먼트화.mask.toCanvasImageSource에서 추출된 정보인 원본 ImageBitmap 객체를 얻을 수 있음을 발견했습니다. 모델별로. 오픈 소스 라이브러리에서 제공하는 기본 구현을 사용하는 대신 ImageBitmap을 마스크로 변환하는 코드를 직접 작성해 보세요.
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)}
2단계와 3단계는 CSS(마스크 이미지)와 협력하기 위해 초상화 영역 외부의 콘텐츠를 검은색으로 채우는 것과 동일합니다(역방향 채우기 ImageBitmap). 그렇지 않으면 사격 시에만 표시됩니다. 초상화 영역에 떠 있습니다(정확히 대상 효과와 반대).
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,提升页面加载性能。
这个两步前端工程已经非常成熟了,略过细节。
注意事项
本期作者
刘俊
Bilibili 수석 개발 엔지니어
위 내용은 웹상의 얼굴 차단 공격에 대한 실시간 보호(머신러닝 기반)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!