import BackgroundFilterProcessor from '../backgroundfilter/BackgroundFilterProcessor' ; import BackgroundFilterSpec from '../backgroundfilter/BackgroundFilterSpec' ; import BackgroundReplacementOptions from './BackgroundReplacementOptions' ; import BackgroundReplacementProcessor from './BackgroundReplacementProcessor' ; import BackgroundReplacementVideoFrameProcessorDelegate from './BackgroundReplacementVideoFrameProcessorDelegate' ; import BackgroundReplacementVideoFrameProcessorObserver from './BackgroundReplacementVideoFrameProcessorObserver' ; /**
 * [[BackgroundReplacementFilter]] implements [[BackgroundReplacementProcessor]].
 * It's a background replacement processor and input is passed into a worker that will apply a segmentation
 * to separate the foreground from the background. Then the background will have a replacement applied.
 *
 * The [[BackgroundReplacementProcessorProvided]] uses WASM and TensorFlow Lite to apply replacement of the
 * background image.
 */ internal export default class BackgroundReplacementFilter extends BackgroundFilterProcessor implements BackgroundReplacementProcessor { private replacementBlob ? : Blob ; private replacementObjectUrl ? : string ; param spec param options constructor ( spec , options ) { super ( 'background replacement' , spec , options , new BackgroundReplacementVideoFrameProcessorDelegate ( ) ) ; this . replacementBlob = options . imageBlob ; this . logger . info ( 'BackgroundReplacement processor successfully created' ) ; this . logger . info ( `BackgroundReplacement spec: ${ this . stringify ( this . spec ) }` ) ; this . logger . info ( `BackgroundReplacement options: ${ this . stringify ( options ) }` ) ; } async setImageBlob ( blob ) { this . replacementBlob = blob ; this . replacementImage = await BackgroundReplacementFilter . loadImage ( this . createReplacementObjectUrl ( ) ) ; } initOnFirstExecution ( ) { } drawImageWithMask ( inputCanvas , mask ) { if ( ! mask ) { mask = new ImageData ( this . spec . model . input . width , this . spec . model . input . height ) ; } const scaledCtx = this . scaledCanvas . getContext ( '2d' ) ; scaledCtx . putImageData ( mask , 0 , 0 ) ; const { canvasCtx , targetCanvas } = this ; const { width , height } = targetCanvas ; canvasCtx . save ( ) ; canvasCtx . clearRect ( 0 , 0 , width , height ) ; canvasCtx . drawImage ( this . scaledCanvas , 0 , 0 , width , height ) ; canvasCtx . globalCompositeOperation = 'source-in' ; canvasCtx . drawImage ( inputCanvas , 0 , 0 , width , height ) ; canvasCtx . globalCompositeOperation = 'destination-over' ; canvasCtx . drawImage ( this . replacementImage , 0 , 0 , targetCanvas . width , targetCanvas . height ) ; canvasCtx . restore ( ) ; } private static loadImageExecutor ( resolve , reject , imageUrl ) { const image = new Image ( ) ; image . crossOrigin = 'Anonymous' ; image . addEventListener ( 'load' , ( ) => { resolve ( image ) ; } , false ) ; image . addEventListener ( 'error' , error => { reject ( new Error ( `Could not load replacement image ${ image . src }: ${ error . message }` ) ) ; } , false ) ; image . src = imageUrl ; } internal static async loadImage ( imageUrl ) { return new Promise < HTMLImageElement > ( ( resolve , reject ) => this . loadImageExecutor ( resolve , reject , imageUrl ) ) ; } private revokeReplacementObjectUrl ( ) { if ( this . replacementObjectUrl ) { URL . revokeObjectURL ( this . replacementObjectUrl ) ; } } private createReplacementObjectUrl ( ) { this . revokeReplacementObjectUrl ( ) ; this . replacementObjectUrl = URL . createObjectURL ( this . replacementBlob ) ; return this . replacementObjectUrl ; } returns throws async loadAssets ( ) { this . replacementImage = await BackgroundReplacementFilter . loadImage ( this . createReplacementObjectUrl ( ) ) ; super . loadAssets ( ) ; return ; } addObserver ( observer ) { this . delegate . addObserver ( observer ) ; } removeObserver ( observer ) { this . delegate . removeObserver ( observer ) ; } async destroy ( ) { super . destroy ( ) ; this . revokeReplacementObjectUrl ( ) ; } }