license import * as faceMesh from '@mediapipe/face_mesh' ; import * as tf from '@tensorflow/tfjs-core' ; import { MEDIAPIPE_KEYPOINTS } from '../constants' ; import { FaceDetector } from '../face_detector' ; import { Keypoint } from '../shared/calculators/interfaces/common_interfaces' ; import { landmarksToDetection } from '../shared/calculators/landmarks_to_detection' ; import { Face , FaceDetectorInput } from '../types' ; import { validateModelConfig } from './detector_utils' ; import { MediaPipeFaceMeshMediaPipeEstimationConfig , MediaPipeFaceMeshMediaPipeModelConfig } from './types' ; /**
 * MediaPipe detector class.
 */ class MediaPipeFaceMeshMediaPipeDetector implements FaceDetector { private readonly faceMeshSolution : faceMesh . FaceMesh ; private width = 0 ; private height = 0 ; private faces : Face [ ] ; private selfieMode = false ; constructor ( config ) { this . faceMeshSolution = new faceMesh . FaceMesh ( { locateFile : ( path , base ) => { if ( config . solutionPath ) { const solutionPath = config . solutionPath . replace ( /\/+$/ , '' ) ; return `${ solutionPath }/${ path }` ; } return `${ base }/${ path }` ; } } ) ; this . faceMeshSolution . setOptions ( { refineLandmarks : config . predictIrises , selfieMode : this . selfieMode , maxNumFaces : config . maxFaces , } ) ; this . faceMeshSolution . onResults ( ( results ) => { this . height = results . image . height ; this . width = results . image . width ; this . faces = [ ] ; if ( results . multiFaceLandmarks !== null ) { const landmarksList = results . multiFaceLandmarks ; for ( let i = 0 ; i < landmarksList . length ; i ++ ) { const keypoints = this . translateOutput ( landmarksList [ i ] ) ; this . faces . push ( { keypoints , box : landmarksToDetection ( keypoints ) . locationData . relativeBoundingBox } ) ; } } } ) ; } private translateOutput ( landmarks ) { const keypoints = landmarks . map ( ( landmark , i ) => { const keypoint = { x : landmark . x * this . width , y : landmark . y * this . height , } ; const name = MEDIAPIPE_KEYPOINTS . get ( i ) ; if ( name != null ) { keypoint . name = name ; } return keypoint ; } ) ; return keypoints ; } param input param config return async estimateFaces ( input , estimationConfig ? ) { if ( estimationConfig && estimationConfig . flipHorizontal && ( estimationConfig . flipHorizontal !== this . selfieMode ) ) { this . selfieMode = estimationConfig . flipHorizontal ; this . faceMeshSolution . setOptions ( { selfieMode : this . selfieMode , } ) ; } input = input instanceof tf . Tensor ? new ImageData ( await tf . browser . toPixels ( input ) , input . shape [ 1 ] , input . shape [ 0 ] ) : input ; await this . faceMeshSolution . send ( { image : input as faceMesh . InputImage } ) ; return this . faces ; } dispose ( ) { this . faceMeshSolution . close ( ) ; } reset ( ) { this . faceMeshSolution . reset ( ) ; this . width = 0 ; this . height = 0 ; this . faces = null ; this . selfieMode = false ; } initialize ( ) { return this . faceMeshSolution . initialize ( ) ; } } param modelConfig export async function load ( modelConfig ) { const config = validateModelConfig ( modelConfig ) ; const detector = new MediaPipeFaceMeshMediaPipeDetector ( config ) ; await detector . initialize ( ) ; return detector ; }