2021-05-25 14:58:20 +02:00
/ * *
2021-09-25 17:51:15 +02:00
* Gesture detection algorithm
2021-05-25 14:58:20 +02:00
* /
2021-09-13 19:28:35 +02:00
import type { GestureResult } from '../result' ;
2021-09-28 18:01:48 +02:00
import * as fingerPose from '../hand/fingerpose' ;
2021-05-22 18:33:19 +02:00
2021-07-29 17:01:50 +02:00
/ * *
* @typedef FaceGesture
* /
export type FaceGesture =
` facing ${ 'left' | 'center' | 'right' } `
| ` blink ${ 'left' | 'right' } eye `
| ` mouth ${ number } % open `
| ` head ${ 'up' | 'down' } ` ;
/ * *
* @typedef IrisGesture
* /
export type IrisGesture =
'facing center'
| ` looking ${ 'left' | 'right' | 'up' | 'down' } `
| 'looking center' ;
/ * *
* @typedef BodyGesture
* /
export type BodyGesture =
` leaning ${ 'left' | 'right' } `
| ` raise ${ 'left' | 'right' } hand `
| 'i give up' ;
/ * *
* @typedef BodyGesture
* /
export type HandGesture =
2021-08-21 02:43:03 +02:00
` ${ 'thumb' | 'index' | 'middle' | 'ring' | 'pinky' } forward `
| ` ${ 'thumb' | 'index' | 'middle' | 'ring' | 'pinky' } up `
| 'victory'
| 'thumbs up' ;
2021-07-29 17:01:50 +02:00
2021-09-12 05:54:35 +02:00
export const body = ( res ) : GestureResult [ ] = > {
2020-11-04 16:18:22 +01:00
if ( ! res ) return [ ] ;
2021-07-29 17:01:50 +02:00
const gestures : Array < { body : number , gesture : BodyGesture } > = [ ] ;
2020-11-26 16:37:04 +01:00
for ( let i = 0 ; i < res . length ; i ++ ) {
2020-11-04 16:18:22 +01:00
// raising hands
2020-11-24 05:36:04 +01:00
const leftWrist = res [ i ] . keypoints . find ( ( a ) = > ( a . part === 'leftWrist' ) ) ;
const rightWrist = res [ i ] . keypoints . find ( ( a ) = > ( a . part === 'rightWrist' ) ) ;
const nose = res [ i ] . keypoints . find ( ( a ) = > ( a . part === 'nose' ) ) ;
2021-10-10 23:52:43 +02:00
if ( nose && leftWrist && rightWrist && ( leftWrist . position [ 1 ] < nose . position [ 1 ] ) && ( rightWrist . position [ 1 ] < nose . position [ 1 ] ) ) gestures . push ( { body : i , gesture : 'i give up' } ) ;
else if ( nose && leftWrist && ( leftWrist . position [ 1 ] < nose . position [ 1 ] ) ) gestures . push ( { body : i , gesture : 'raise left hand' } ) ;
else if ( nose && rightWrist && ( rightWrist . position [ 1 ] < nose . position [ 1 ] ) ) gestures . push ( { body : i , gesture : 'raise right hand' } ) ;
2020-11-04 16:18:22 +01:00
// leaning
2020-11-24 05:36:04 +01:00
const leftShoulder = res [ i ] . keypoints . find ( ( a ) = > ( a . part === 'leftShoulder' ) ) ;
const rightShoulder = res [ i ] . keypoints . find ( ( a ) = > ( a . part === 'rightShoulder' ) ) ;
2021-10-10 23:52:43 +02:00
if ( leftShoulder && rightShoulder ) gestures . push ( { body : i , gesture : ` leaning ${ ( leftShoulder . position [ 1 ] > rightShoulder . position [ 1 ] ) ? 'left' : 'right' } ` } ) ;
2020-11-04 16:18:22 +01:00
}
return gestures ;
} ;
2021-09-12 05:54:35 +02:00
export const face = ( res ) : GestureResult [ ] = > {
2020-11-04 16:18:22 +01:00
if ( ! res ) return [ ] ;
2021-07-29 17:01:50 +02:00
const gestures : Array < { face : number , gesture : FaceGesture } > = [ ] ;
2020-11-26 16:37:04 +01:00
for ( let i = 0 ; i < res . length ; i ++ ) {
2021-09-28 18:01:48 +02:00
if ( res [ i ] . mesh && res [ i ] . mesh . length > 450 ) {
2021-03-06 23:22:47 +01:00
const eyeFacing = res [ i ] . mesh [ 33 ] [ 2 ] - res [ i ] . mesh [ 263 ] [ 2 ] ;
2021-04-19 01:33:40 +02:00
if ( Math . abs ( eyeFacing ) < 10 ) gestures . push ( { face : i , gesture : 'facing center' } ) ;
2021-04-19 22:02:47 +02:00
else gestures . push ( { face : i , gesture : ` facing ${ eyeFacing < 0 ? 'left' : 'right' } ` } ) ;
2020-11-24 05:36:04 +01:00
const openLeft = Math . abs ( res [ i ] . mesh [ 374 ] [ 1 ] - res [ i ] . mesh [ 386 ] [ 1 ] ) / Math . abs ( res [ i ] . mesh [ 443 ] [ 1 ] - res [ i ] . mesh [ 450 ] [ 1 ] ) ; // center of eye inner lid y coord div center of wider eye border y coord
if ( openLeft < 0.2 ) gestures . push ( { face : i , gesture : 'blink left eye' } ) ;
const openRight = Math . abs ( res [ i ] . mesh [ 145 ] [ 1 ] - res [ i ] . mesh [ 159 ] [ 1 ] ) / Math . abs ( res [ i ] . mesh [ 223 ] [ 1 ] - res [ i ] . mesh [ 230 ] [ 1 ] ) ; // center of eye inner lid y coord div center of wider eye border y coord
if ( openRight < 0.2 ) gestures . push ( { face : i , gesture : 'blink right eye' } ) ;
const mouthOpen = Math . min ( 100 , 500 * Math . abs ( res [ i ] . mesh [ 13 ] [ 1 ] - res [ i ] . mesh [ 14 ] [ 1 ] ) / Math . abs ( res [ i ] . mesh [ 10 ] [ 1 ] - res [ i ] . mesh [ 152 ] [ 1 ] ) ) ;
if ( mouthOpen > 10 ) gestures . push ( { face : i , gesture : ` mouth ${ Math . trunc ( mouthOpen ) } % open ` } ) ;
const chinDepth = res [ i ] . mesh [ 152 ] [ 2 ] ;
if ( Math . abs ( chinDepth ) > 10 ) gestures . push ( { face : i , gesture : ` head ${ chinDepth < 0 ? 'up' : 'down' } ` } ) ;
2020-11-09 20:26:10 +01:00
}
2020-11-04 16:18:22 +01:00
}
return gestures ;
} ;
2021-09-12 05:54:35 +02:00
export const iris = ( res ) : GestureResult [ ] = > {
2021-01-11 15:02:02 +01:00
if ( ! res ) return [ ] ;
2021-07-29 17:01:50 +02:00
const gestures : Array < { iris : number , gesture : IrisGesture } > = [ ] ;
2021-01-11 15:02:02 +01:00
for ( let i = 0 ; i < res . length ; i ++ ) {
2021-09-28 18:01:48 +02:00
if ( ! res [ i ] . annotations || ! res [ i ] . annotations . leftEyeIris || ! res [ i ] . annotations . leftEyeIris [ 0 ] || ! res [ i ] . annotations . rightEyeIris || ! res [ i ] . annotations . rightEyeIris [ 0 ] ) continue ;
2021-01-11 15:02:02 +01:00
const sizeXLeft = res [ i ] . annotations . leftEyeIris [ 3 ] [ 0 ] - res [ i ] . annotations . leftEyeIris [ 1 ] [ 0 ] ;
const sizeYLeft = res [ i ] . annotations . leftEyeIris [ 4 ] [ 1 ] - res [ i ] . annotations . leftEyeIris [ 2 ] [ 1 ] ;
const areaLeft = Math . abs ( sizeXLeft * sizeYLeft ) ;
const sizeXRight = res [ i ] . annotations . rightEyeIris [ 3 ] [ 0 ] - res [ i ] . annotations . rightEyeIris [ 1 ] [ 0 ] ;
const sizeYRight = res [ i ] . annotations . rightEyeIris [ 4 ] [ 1 ] - res [ i ] . annotations . rightEyeIris [ 2 ] [ 1 ] ;
const areaRight = Math . abs ( sizeXRight * sizeYRight ) ;
2021-04-19 15:30:04 +02:00
let center = false ;
2021-01-11 15:02:02 +01:00
const difference = Math . abs ( areaLeft - areaRight ) / Math . max ( areaLeft , areaRight ) ;
2021-04-19 15:30:04 +02:00
if ( difference < 0.25 ) {
center = true ;
gestures . push ( { iris : i , gesture : 'facing center' } ) ;
}
2021-04-19 01:33:40 +02:00
2021-05-28 16:43:48 +02:00
const rightIrisCenterX = Math . abs ( res [ i ] . mesh [ 33 ] [ 0 ] - res [ i ] . annotations . rightEyeIris [ 0 ] [ 0 ] ) / res [ i ] . box [ 2 ] ;
const leftIrisCenterX = Math . abs ( res [ i ] . mesh [ 263 ] [ 0 ] - res [ i ] . annotations . leftEyeIris [ 0 ] [ 0 ] ) / res [ i ] . box [ 2 ] ;
if ( leftIrisCenterX > 0.06 || rightIrisCenterX > 0.06 ) center = false ;
2021-11-02 16:42:15 +01:00
if ( leftIrisCenterX > 0.05 ) gestures . push ( { iris : i , gesture : 'looking right' } ) ;
if ( rightIrisCenterX > 0.05 ) gestures . push ( { iris : i , gesture : 'looking left' } ) ;
2021-04-19 15:30:04 +02:00
2021-05-28 16:43:48 +02:00
const rightIrisCenterY = Math . abs ( res [ i ] . mesh [ 145 ] [ 1 ] - res [ i ] . annotations . rightEyeIris [ 0 ] [ 1 ] ) / res [ i ] . box [ 3 ] ;
const leftIrisCenterY = Math . abs ( res [ i ] . mesh [ 374 ] [ 1 ] - res [ i ] . annotations . leftEyeIris [ 0 ] [ 1 ] ) / res [ i ] . box [ 3 ] ;
2021-05-28 21:53:51 +02:00
if ( leftIrisCenterY < 0.01 || rightIrisCenterY < 0.01 || leftIrisCenterY > 0.022 || rightIrisCenterY > 0.022 ) center = false ;
2021-05-28 16:43:48 +02:00
if ( leftIrisCenterY < 0.01 || rightIrisCenterY < 0.01 ) gestures . push ( { iris : i , gesture : 'looking down' } ) ;
2021-05-28 21:53:51 +02:00
if ( leftIrisCenterY > 0.022 || rightIrisCenterY > 0.022 ) gestures . push ( { iris : i , gesture : 'looking up' } ) ;
2021-04-19 15:30:04 +02:00
// still center;
if ( center ) gestures . push ( { iris : i , gesture : 'looking center' } ) ;
2021-01-11 15:02:02 +01:00
}
return gestures ;
} ;
2021-09-12 05:54:35 +02:00
export const hand = ( res ) : GestureResult [ ] = > {
2020-11-04 16:18:22 +01:00
if ( ! res ) return [ ] ;
2021-07-29 17:01:50 +02:00
const gestures : Array < { hand : number , gesture : HandGesture } > = [ ] ;
2020-11-26 16:37:04 +01:00
for ( let i = 0 ; i < res . length ; i ++ ) {
2021-02-13 15:16:41 +01:00
const fingers : Array < { name : string , position : number } > = [ ] ;
2021-09-21 22:48:16 +02:00
if ( res [ i ] [ 'annotations' ] ) {
for ( const [ finger , pos ] of Object . entries ( res [ i ] [ 'annotations' ] ) ) {
if ( finger !== 'palmBase' && Array . isArray ( pos ) && pos [ 0 ] ) fingers . push ( { name : finger.toLowerCase ( ) , position : pos [ 0 ] } ) ; // get tip of each finger
}
2020-11-04 16:18:22 +01:00
}
2020-11-04 20:59:30 +01:00
if ( fingers && fingers . length > 0 ) {
const closest = fingers . reduce ( ( best , a ) = > ( best . position [ 2 ] < a . position [ 2 ] ? best : a ) ) ;
2021-07-29 17:01:50 +02:00
gestures . push ( { hand : i , gesture : ` ${ closest . name } forward ` as HandGesture } ) ;
2020-11-04 20:59:30 +01:00
const highest = fingers . reduce ( ( best , a ) = > ( best . position [ 1 ] < a . position [ 1 ] ? best : a ) ) ;
2021-07-29 17:01:50 +02:00
gestures . push ( { hand : i , gesture : ` ${ highest . name } up ` as HandGesture } ) ;
2020-11-04 20:59:30 +01:00
}
2021-09-21 22:48:16 +02:00
if ( res [ i ] [ 'keypoints' ] ) {
const poses = fingerPose . match ( res [ i ] [ 'keypoints' ] ) ;
for ( const pose of poses ) gestures . push ( { hand : i , gesture : pose.name as HandGesture } ) ;
}
2020-11-04 16:18:22 +01:00
}
return gestures ;
} ;