-
Notifications
You must be signed in to change notification settings - Fork 472
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add demo for tensorflow bodypix (#1027)
- Loading branch information
Showing
6 changed files
with
156 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
demos/browser/app/meetingV2/videofilter/SegmentationProcessor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// eslint-disable-next-line | ||
declare var bodyPix: any; | ||
|
||
import { CanvasVideoFrameBuffer, VideoFrameBuffer, VideoFrameProcessor } from 'amazon-chime-sdk-js'; | ||
|
||
export default class SegmentationProcessor implements VideoFrameProcessor { | ||
private targetCanvas: HTMLCanvasElement = document.createElement('canvas') as HTMLCanvasElement; | ||
private canvasVideoFrameBuffer = new CanvasVideoFrameBuffer(this.targetCanvas); | ||
private sourceWidth: number = 0; | ||
private sourceHeight: number = 0; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
private mask: any | undefined = undefined; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
private model: any | undefined = undefined; | ||
|
||
constructor() {} | ||
|
||
async process(buffers: VideoFrameBuffer[]): Promise<VideoFrameBuffer[]> { | ||
if (!this.model) { | ||
this.model = await bodyPix.load(); | ||
} | ||
|
||
const inputCanvas = buffers[0].asCanvasElement(); | ||
if (!inputCanvas) { | ||
throw new Error('buffer is already destroyed'); | ||
} | ||
|
||
const frameWidth = inputCanvas.width; | ||
const frameHeight = inputCanvas.height; | ||
if (frameWidth === 0 || frameHeight === 0) { | ||
return buffers; | ||
} | ||
|
||
if (this.sourceWidth !== frameWidth || this.sourceHeight !== frameHeight) { | ||
this.sourceWidth = frameWidth; | ||
this.sourceHeight = frameHeight; | ||
|
||
// update target canvas size to match the frame size | ||
this.targetCanvas.width = this.sourceWidth; | ||
this.targetCanvas.height = this.sourceHeight; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
let predictions = await this.model.segmentPerson(inputCanvas); | ||
if (predictions) { | ||
const foregroundColor = { r: 255, g: 255, b: 255, a: 255 }; | ||
const backgroundColor = { r: 0, g: 0, b: 0, a: 255 }; | ||
this.mask = bodyPix.toMask(predictions, foregroundColor, backgroundColor, true); | ||
} | ||
|
||
if (this.mask) { | ||
bodyPix.drawMask(this.targetCanvas, inputCanvas as HTMLCanvasElement, this.mask); | ||
buffers[0] = this.canvasVideoFrameBuffer; | ||
} | ||
predictions = undefined; | ||
return buffers; | ||
} | ||
|
||
async destroy(): Promise<void> { | ||
if (this.model) { | ||
this.model.dispose(); | ||
} | ||
this.model = undefined; | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
demos/browser/app/meetingV2/videofilter/SegmentationUtil.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { detect } from 'detect-browser'; | ||
|
||
const SEGMENTATION_DEPENDENCIES = [ | ||
{ | ||
src: 'https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js', | ||
integrity: 'sha384-uI1PW0SEa/QzAUuRQ6Bz5teBONsa9D0ZbVxMcM8mu4IjJ5msHyM7RRtZtL8LnSf3', | ||
crossOrigin: 'anonymous', | ||
}, | ||
|
||
{ | ||
src: 'https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf-core.min.js', | ||
integrity: 'sha384-DlI/SVdTGUBY5hi4h0p+nmC6V8i0FW5Nya/gYElz0L68HrSiXsBh+rqWcoZx3SXY', | ||
crossOrigin: 'anonymous', | ||
}, | ||
|
||
{ | ||
src: | ||
'https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf-backend-webgl.min.js', | ||
integrity: 'sha384-21TV9Kpzn8SF68G1U6nYN3qPZnb97F06JuW4v0FDDBzW+CUwv8GcKMR+BjnE7Vmm', | ||
crossOrigin: 'anonymous', | ||
}, | ||
|
||
{ | ||
src: 'https://cdn.jsdelivr.net/npm/@tensorflow-models/[email protected]/dist/body-pix.min.js', | ||
integrity: 'sha384-dPJ/sICXCqdh39bsuGLVFcOiRyeL/XcFrwiFrJq9oh7k1TCtsUKhX6UV2X4UuKU4', | ||
crossOrigin: 'anonymous', | ||
}, | ||
]; | ||
|
||
|
||
export async function loadBodyPixDependency(timeoutMs: number): Promise<void> { | ||
// the tf library loading order must be followed | ||
for (let i = 0; i < SEGMENTATION_DEPENDENCIES.length; i++) { | ||
const dep = SEGMENTATION_DEPENDENCIES[i]; | ||
await new Promise<void>((resolve, reject) => { | ||
let script = document.createElement('script'); | ||
const timer = setTimeout(() => { | ||
reject(new Error(`Loading script ${dep.src} takes longer than ${timeoutMs}`)); | ||
}, timeoutMs); | ||
script.onload = function(_ev) { | ||
clearTimeout(timer); | ||
resolve(); | ||
}; | ||
script.src = dep.src; | ||
script.integrity = dep.integrity; | ||
script.crossOrigin = dep.crossOrigin; | ||
document.body.appendChild(script); | ||
}); | ||
} | ||
} | ||
|
||
export function platformCanSupportBodyPixWithoutDegradation(): boolean { | ||
// https://blog.tensorflow.org/2019/11/updated-bodypix-2.html for more detail on performance | ||
// https://github.com/tensorflow/tfjs/issues/3319 which results in firefox memory leak | ||
const browser = detect(); | ||
return browser.name === 'chrome' && /(android)/i.test(navigator.userAgent) === false; | ||
} |