2021-05-25 14:58:20 +02:00
/ * *
* Module that implements helper draw functions , exposed as human . draw
* /
2021-03-17 23:23:19 +01:00
import { TRI468 as triangulation } from '../blazeface/coords' ;
2021-04-13 17:05:52 +02:00
import { mergeDeep } from '../helpers' ;
2021-05-24 17:10:13 +02:00
import type { Result , Face , Body , Hand , Item , Gesture , Person } from '../result' ;
2021-03-05 17:43:50 +01:00
2021-04-13 17:05:52 +02:00
/ * *
* Draw Options
* Accessed via ` human.draw.options ` or provided per each draw method as the drawOptions optional parameter
* - color : draw color
* - labelColor : color for labels
* - shadowColor : optional shadow color for labels
* - font : font for labels
* - lineHeight : line height for labels , used for multi - line labels ,
* - lineWidth : width of any lines ,
* - pointSize : size of any point ,
* - roundRect : for boxes , round corners by this many pixels ,
* - drawPoints : should points be drawn ,
* - drawLabels : should labels be drawn ,
* - drawBoxes : should boxes be drawn ,
* - drawPolygons : should polygons be drawn ,
* - fillPolygons : should drawn polygons be filled ,
* - useDepth : use z - axis coordinate as color shade ,
* - useCurves : draw polygons as cures or as lines ,
* - bufferedOutput : experimental : allows to call draw methods multiple times for each detection and interpolate results between results thus achieving smoother animations
2021-05-23 19:52:49 +02:00
* - bufferedFactor : speed of interpolation convergence where 1 means 100 % immediately , 2 means 50 % at each interpolation , etc .
2021-04-13 17:05:52 +02:00
* /
export interface DrawOptions {
color : string ,
labelColor : string ,
shadowColor : string ,
font : string ,
lineHeight : number ,
lineWidth : number ,
pointSize : number ,
roundRect : number ,
2021-05-23 03:47:59 +02:00
drawPoints : boolean ,
drawLabels : boolean ,
drawBoxes : boolean ,
drawPolygons : boolean ,
2021-05-29 15:20:01 +02:00
drawGaze : boolean ,
2021-05-23 03:47:59 +02:00
fillPolygons : boolean ,
useDepth : boolean ,
useCurves : boolean ,
bufferedOutput : boolean ,
2021-05-23 19:52:49 +02:00
bufferedFactor : number ,
2021-04-13 17:05:52 +02:00
}
export const options : DrawOptions = {
2021-03-14 04:31:09 +01:00
color : < string > 'rgba(173, 216, 230, 0.3)' , // 'lightblue' with light alpha channel
labelColor : < string > 'rgba(173, 216, 230, 1)' , // 'lightblue' with dark alpha channel
shadowColor : < string > 'black' ,
2021-05-28 16:43:48 +02:00
font : < string > 'small-caps 14px "Segoe UI"' ,
2021-04-19 15:30:04 +02:00
lineHeight : < number > 24 ,
2021-03-14 04:31:09 +01:00
lineWidth : < number > 6 ,
pointSize : < number > 2 ,
roundRect : < number > 28 ,
2021-05-23 03:47:59 +02:00
drawPoints : < boolean > false ,
drawLabels : < boolean > true ,
drawBoxes : < boolean > true ,
drawPolygons : < boolean > true ,
2021-05-29 15:20:01 +02:00
drawGaze : < boolean > false ,
2021-05-23 03:47:59 +02:00
fillPolygons : < boolean > false ,
useDepth : < boolean > true ,
useCurves : < boolean > false ,
2021-05-23 19:52:49 +02:00
bufferedFactor : < number > 2 ,
bufferedOutput : < boolean > false ,
2021-03-05 17:43:50 +01:00
} ;
2021-05-24 17:10:13 +02:00
let bufferedResult : Result = { face : [ ] , body : [ ] , hand : [ ] , gesture : [ ] , object : [ ] , persons : [ ] , performance : { } , timestamp : 0 } ;
2021-05-22 19:17:07 +02:00
2021-04-13 17:05:52 +02:00
function point ( ctx , x , y , z = 0 , localOptions ) {
ctx . fillStyle = localOptions . useDepth && z ? ` rgba( ${ 127.5 + ( 2 * z ) } , ${ 127.5 - ( 2 * z ) } , 255, 0.3) ` : localOptions . color ;
2021-03-05 17:43:50 +01:00
ctx . beginPath ( ) ;
2021-04-13 17:05:52 +02:00
ctx . arc ( x , y , localOptions . pointSize , 0 , 2 * Math . PI ) ;
2021-03-05 17:43:50 +01:00
ctx . fill ( ) ;
}
2021-04-13 17:05:52 +02:00
function rect ( ctx , x , y , width , height , localOptions ) {
2021-03-06 16:38:04 +01:00
ctx . beginPath ( ) ;
2021-04-13 17:05:52 +02:00
if ( localOptions . useCurves ) {
2021-03-06 16:38:04 +01:00
const cx = ( x + x + width ) / 2 ;
const cy = ( y + y + height ) / 2 ;
ctx . ellipse ( cx , cy , width / 2 , height / 2 , 0 , 0 , 2 * Math . PI ) ;
} else {
2021-04-13 17:05:52 +02:00
ctx . lineWidth = localOptions . lineWidth ;
ctx . moveTo ( x + localOptions . roundRect , y ) ;
ctx . lineTo ( x + width - localOptions . roundRect , y ) ;
ctx . quadraticCurveTo ( x + width , y , x + width , y + localOptions . roundRect ) ;
ctx . lineTo ( x + width , y + height - localOptions . roundRect ) ;
ctx . quadraticCurveTo ( x + width , y + height , x + width - localOptions . roundRect , y + height ) ;
ctx . lineTo ( x + localOptions . roundRect , y + height ) ;
ctx . quadraticCurveTo ( x , y + height , x , y + height - localOptions . roundRect ) ;
ctx . lineTo ( x , y + localOptions . roundRect ) ;
ctx . quadraticCurveTo ( x , y , x + localOptions . roundRect , y ) ;
2021-03-05 17:43:50 +01:00
ctx . closePath ( ) ;
}
2021-03-06 16:38:04 +01:00
ctx . stroke ( ) ;
2021-03-05 17:43:50 +01:00
}
2021-05-22 20:53:51 +02:00
function lines ( ctx , points : [ number , number , number ? ] [ ] = [ ] , localOptions ) {
2021-03-06 16:38:04 +01:00
if ( points === undefined || points . length === 0 ) return ;
2021-03-05 20:30:09 +01:00
ctx . beginPath ( ) ;
2021-03-06 16:38:04 +01:00
ctx . moveTo ( points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] ) ;
2021-03-12 22:43:36 +01:00
for ( const pt of points ) {
2021-05-22 20:53:51 +02:00
const z = pt [ 2 ] || 0 ;
ctx . strokeStyle = localOptions . useDepth && z ? ` rgba( ${ 127.5 + ( 2 * z ) } , ${ 127.5 - ( 2 * z ) } , 255, 0.3) ` : localOptions . color ;
ctx . fillStyle = localOptions . useDepth && z ? ` rgba( ${ 127.5 + ( 2 * z ) } , ${ 127.5 - ( 2 * z ) } , 255, 0.3) ` : localOptions . color ;
2021-05-22 18:33:19 +02:00
ctx . lineTo ( pt [ 0 ] , Math . round ( pt [ 1 ] ) ) ;
2021-03-12 22:43:36 +01:00
}
2021-03-06 16:38:04 +01:00
ctx . stroke ( ) ;
2021-04-13 17:05:52 +02:00
if ( localOptions . fillPolygons ) {
2021-03-05 20:30:09 +01:00
ctx . closePath ( ) ;
2021-03-06 16:38:04 +01:00
ctx . fill ( ) ;
2021-03-05 20:30:09 +01:00
}
}
2021-05-22 20:53:51 +02:00
function curves ( ctx , points : [ number , number , number ? ] [ ] = [ ] , localOptions ) {
2021-03-06 16:38:04 +01:00
if ( points === undefined || points . length === 0 ) return ;
2021-04-13 17:05:52 +02:00
if ( ! localOptions . useCurves || points . length <= 2 ) {
lines ( ctx , points , localOptions ) ;
2021-03-06 16:38:04 +01:00
return ;
2021-03-05 17:43:50 +01:00
}
ctx . moveTo ( points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] ) ;
2021-03-06 16:38:04 +01:00
for ( let i = 0 ; i < points . length - 2 ; i ++ ) {
const xc = ( points [ i ] [ 0 ] + points [ i + 1 ] [ 0 ] ) / 2 ;
const yc = ( points [ i ] [ 1 ] + points [ i + 1 ] [ 1 ] ) / 2 ;
ctx . quadraticCurveTo ( points [ i ] [ 0 ] , points [ i ] [ 1 ] , xc , yc ) ;
2021-03-05 17:43:50 +01:00
}
2021-03-06 16:38:04 +01:00
ctx . quadraticCurveTo ( points [ points . length - 2 ] [ 0 ] , points [ points . length - 2 ] [ 1 ] , points [ points . length - 1 ] [ 0 ] , points [ points . length - 1 ] [ 1 ] ) ;
2021-03-05 17:43:50 +01:00
ctx . stroke ( ) ;
2021-04-13 17:05:52 +02:00
if ( localOptions . fillPolygons ) {
2021-03-06 16:38:04 +01:00
ctx . closePath ( ) ;
ctx . fill ( ) ;
2021-03-05 17:43:50 +01:00
}
}
2021-05-22 18:33:19 +02:00
export async function gesture ( inCanvas : HTMLCanvasElement , result : Array < Gesture > , drawOptions? : DrawOptions ) {
2021-04-13 17:05:52 +02:00
const localOptions = mergeDeep ( options , drawOptions ) ;
2021-03-05 17:43:50 +01:00
if ( ! result || ! inCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) ) return ;
const ctx = inCanvas . getContext ( '2d' ) ;
if ( ! ctx ) return ;
2021-04-13 17:05:52 +02:00
ctx . font = localOptions . font ;
ctx . fillStyle = localOptions . color ;
2021-03-05 17:43:50 +01:00
let i = 1 ;
for ( let j = 0 ; j < result . length ; j ++ ) {
2021-05-23 03:47:59 +02:00
let where : unknown [ ] = [ ] ; // what&where is a record
let what : unknown [ ] = [ ] ; // what&where is a record
2021-03-05 17:43:50 +01:00
[ where , what ] = Object . entries ( result [ j ] ) ;
2021-05-23 03:47:59 +02:00
if ( ( what . length > 1 ) && ( ( what [ 1 ] as string ) . length > 0 ) ) {
2021-05-24 17:10:13 +02:00
const who = where [ 1 ] as number > 0 ? ` # ${ where [ 1 ] } ` : '' ;
const label = ` ${ where [ 0 ] } ${ who } : ${ what [ 1 ] } ` ;
2021-04-13 17:05:52 +02:00
if ( localOptions . shadowColor && localOptions . shadowColor !== '' ) {
ctx . fillStyle = localOptions . shadowColor ;
ctx . fillText ( label , 8 , 2 + ( i * localOptions . lineHeight ) ) ;
2021-03-05 17:43:50 +01:00
}
2021-04-13 17:05:52 +02:00
ctx . fillStyle = localOptions . labelColor ;
ctx . fillText ( label , 6 , 0 + ( i * localOptions . lineHeight ) ) ;
2021-03-05 17:43:50 +01:00
i += 1 ;
}
}
}
2021-05-22 18:33:19 +02:00
export async function face ( inCanvas : HTMLCanvasElement , result : Array < Face > , drawOptions? : DrawOptions ) {
2021-04-13 17:05:52 +02:00
const localOptions = mergeDeep ( options , drawOptions ) ;
2021-03-05 17:43:50 +01:00
if ( ! result || ! inCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) ) return ;
const ctx = inCanvas . getContext ( '2d' ) ;
if ( ! ctx ) return ;
for ( const f of result ) {
2021-04-13 17:05:52 +02:00
ctx . font = localOptions . font ;
ctx . strokeStyle = localOptions . color ;
ctx . fillStyle = localOptions . color ;
2021-05-25 14:58:20 +02:00
if ( localOptions . drawBoxes ) rect ( ctx , f . box [ 0 ] , f . box [ 1 ] , f . box [ 2 ] , f . box [ 3 ] , localOptions ) ;
2021-03-05 17:43:50 +01:00
// silly hack since fillText does not suport new line
const labels :string [ ] = [ ] ;
labels . push ( ` face confidence: ${ Math . trunc ( 100 * f . confidence ) } % ` ) ;
if ( f . genderConfidence ) labels . push ( ` ${ f . gender || '' } ${ Math . trunc ( 100 * f . genderConfidence ) } % confident ` ) ;
// if (f.genderConfidence) labels.push(f.gender);
if ( f . age ) labels . push ( ` age: ${ f . age || '' } ` ) ;
if ( f . iris ) labels . push ( ` iris distance: ${ f . iris } ` ) ;
if ( f . emotion && f . emotion . length > 0 ) {
const emotion = f . emotion . map ( ( a ) = > ` ${ Math . trunc ( 100 * a . score ) } % ${ a . emotion } ` ) ;
labels . push ( emotion . join ( ' ' ) ) ;
}
2021-03-28 14:40:39 +02:00
if ( f . rotation && f . rotation . angle && f . rotation . angle . roll ) labels . push ( ` roll: ${ Math . trunc ( 100 * f . rotation . angle . roll ) / 100 } yaw: ${ Math . trunc ( 100 * f . rotation . angle . yaw ) / 100 } pitch: ${ Math . trunc ( 100 * f . rotation . angle . pitch ) / 100 } ` ) ;
2021-03-05 17:43:50 +01:00
if ( labels . length === 0 ) labels . push ( 'face' ) ;
2021-04-13 17:05:52 +02:00
ctx . fillStyle = localOptions . color ;
2021-03-05 17:43:50 +01:00
for ( let i = labels . length - 1 ; i >= 0 ; i -- ) {
const x = Math . max ( f . box [ 0 ] , 0 ) ;
2021-04-13 17:05:52 +02:00
const y = i * localOptions . lineHeight + f . box [ 1 ] ;
if ( localOptions . shadowColor && localOptions . shadowColor !== '' ) {
ctx . fillStyle = localOptions . shadowColor ;
2021-03-05 17:43:50 +01:00
ctx . fillText ( labels [ i ] , x + 5 , y + 16 ) ;
}
2021-04-13 17:05:52 +02:00
ctx . fillStyle = localOptions . labelColor ;
2021-03-05 17:43:50 +01:00
ctx . fillText ( labels [ i ] , x + 4 , y + 15 ) ;
}
ctx . lineWidth = 1 ;
2021-03-17 16:32:37 +01:00
if ( f . mesh && f . mesh . length > 0 ) {
2021-04-13 17:05:52 +02:00
if ( localOptions . drawPoints ) {
for ( const pt of f . mesh ) point ( ctx , pt [ 0 ] , pt [ 1 ] , pt [ 2 ] , localOptions ) ;
2021-03-12 22:43:36 +01:00
// for (const pt of f.meshRaw) point(ctx, pt[0] * inCanvas.offsetWidth, pt[1] * inCanvas.offsetHeight, pt[2]);
2021-03-05 17:43:50 +01:00
}
2021-04-13 17:05:52 +02:00
if ( localOptions . drawPolygons ) {
2021-03-12 22:43:36 +01:00
ctx . lineWidth = 1 ;
2021-03-05 17:43:50 +01:00
for ( let i = 0 ; i < triangulation . length / 3 ; i ++ ) {
const points = [
triangulation [ i * 3 + 0 ] ,
triangulation [ i * 3 + 1 ] ,
triangulation [ i * 3 + 2 ] ,
] . map ( ( index ) = > f . mesh [ index ] ) ;
2021-04-13 17:05:52 +02:00
lines ( ctx , points , localOptions ) ;
2021-03-05 17:43:50 +01:00
}
// iris: array[center, left, top, right, bottom]
2021-05-22 18:33:19 +02:00
if ( f . annotations && f . annotations [ 'leftEyeIris' ] ) {
2021-04-13 17:05:52 +02:00
ctx . strokeStyle = localOptions . useDepth ? 'rgba(255, 200, 255, 0.3)' : localOptions . color ;
2021-03-05 17:43:50 +01:00
ctx . beginPath ( ) ;
2021-05-22 18:33:19 +02:00
const sizeX = Math . abs ( f . annotations [ 'leftEyeIris' ] [ 3 ] [ 0 ] - f . annotations [ 'leftEyeIris' ] [ 1 ] [ 0 ] ) / 2 ;
const sizeY = Math . abs ( f . annotations [ 'leftEyeIris' ] [ 4 ] [ 1 ] - f . annotations [ 'leftEyeIris' ] [ 2 ] [ 1 ] ) / 2 ;
ctx . ellipse ( f . annotations [ 'leftEyeIris' ] [ 0 ] [ 0 ] , f . annotations [ 'leftEyeIris' ] [ 0 ] [ 1 ] , sizeX , sizeY , 0 , 0 , 2 * Math . PI ) ;
2021-03-05 17:43:50 +01:00
ctx . stroke ( ) ;
2021-04-13 17:05:52 +02:00
if ( localOptions . fillPolygons ) {
ctx . fillStyle = localOptions . useDepth ? 'rgba(255, 255, 200, 0.3)' : localOptions . color ;
2021-03-05 17:43:50 +01:00
ctx . fill ( ) ;
}
}
2021-05-22 18:33:19 +02:00
if ( f . annotations && f . annotations [ 'rightEyeIris' ] ) {
2021-04-13 17:05:52 +02:00
ctx . strokeStyle = localOptions . useDepth ? 'rgba(255, 200, 255, 0.3)' : localOptions . color ;
2021-03-05 17:43:50 +01:00
ctx . beginPath ( ) ;
2021-05-22 18:33:19 +02:00
const sizeX = Math . abs ( f . annotations [ 'rightEyeIris' ] [ 3 ] [ 0 ] - f . annotations [ 'rightEyeIris' ] [ 1 ] [ 0 ] ) / 2 ;
const sizeY = Math . abs ( f . annotations [ 'rightEyeIris' ] [ 4 ] [ 1 ] - f . annotations [ 'rightEyeIris' ] [ 2 ] [ 1 ] ) / 2 ;
ctx . ellipse ( f . annotations [ 'rightEyeIris' ] [ 0 ] [ 0 ] , f . annotations [ 'rightEyeIris' ] [ 0 ] [ 1 ] , sizeX , sizeY , 0 , 0 , 2 * Math . PI ) ;
2021-03-05 17:43:50 +01:00
ctx . stroke ( ) ;
2021-04-13 17:05:52 +02:00
if ( localOptions . fillPolygons ) {
ctx . fillStyle = localOptions . useDepth ? 'rgba(255, 255, 200, 0.3)' : localOptions . color ;
2021-03-05 17:43:50 +01:00
ctx . fill ( ) ;
}
}
2021-05-29 15:20:01 +02:00
if ( localOptions . drawGaze && f . rotation ? . gaze ? . strength && f . rotation ? . gaze ? . angle ) {
2021-05-28 21:53:51 +02:00
const leftGaze = [
f . annotations [ 'leftEyeIris' ] [ 0 ] [ 0 ] + ( Math . cos ( f . rotation . gaze . angle ) * f . rotation . gaze . strength * f . box [ 2 ] ) ,
f . annotations [ 'leftEyeIris' ] [ 0 ] [ 1 ] - ( Math . sin ( f . rotation . gaze . angle ) * f . rotation . gaze . strength * f . box [ 3 ] ) ,
] ;
ctx . beginPath ( ) ;
ctx . moveTo ( f . annotations [ 'leftEyeIris' ] [ 0 ] [ 0 ] , f . annotations [ 'leftEyeIris' ] [ 0 ] [ 1 ] ) ;
ctx . strokeStyle = 'pink' ;
ctx . lineTo ( leftGaze [ 0 ] , leftGaze [ 1 ] ) ;
ctx . stroke ( ) ;
const rightGaze = [
f . annotations [ 'rightEyeIris' ] [ 0 ] [ 0 ] + ( Math . cos ( f . rotation . gaze . angle ) * f . rotation . gaze . strength * f . box [ 2 ] ) ,
f . annotations [ 'rightEyeIris' ] [ 0 ] [ 1 ] - ( Math . sin ( f . rotation . gaze . angle ) * f . rotation . gaze . strength * f . box [ 3 ] ) ,
] ;
ctx . beginPath ( ) ;
ctx . moveTo ( f . annotations [ 'rightEyeIris' ] [ 0 ] [ 0 ] , f . annotations [ 'rightEyeIris' ] [ 0 ] [ 1 ] ) ;
ctx . strokeStyle = 'pink' ;
ctx . lineTo ( rightGaze [ 0 ] , rightGaze [ 1 ] ) ;
ctx . stroke ( ) ;
}
2021-03-05 17:43:50 +01:00
}
}
}
}
2021-05-22 18:33:19 +02:00
export async function body ( inCanvas : HTMLCanvasElement , result : Array < Body > , drawOptions? : DrawOptions ) {
2021-04-13 17:05:52 +02:00
const localOptions = mergeDeep ( options , drawOptions ) ;
2021-03-05 17:43:50 +01:00
if ( ! result || ! inCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) ) return ;
const ctx = inCanvas . getContext ( '2d' ) ;
if ( ! ctx ) return ;
ctx . lineJoin = 'round' ;
for ( let i = 0 ; i < result . length ; i ++ ) {
2021-04-13 17:05:52 +02:00
ctx . strokeStyle = localOptions . color ;
2021-04-24 17:49:26 +02:00
ctx . fillStyle = localOptions . color ;
2021-04-13 17:05:52 +02:00
ctx . lineWidth = localOptions . lineWidth ;
2021-04-24 17:49:26 +02:00
ctx . font = localOptions . font ;
2021-05-22 18:33:19 +02:00
if ( localOptions . drawBoxes && result [ i ] . box && result [ i ] . box ? . length === 4 ) {
// @ts-ignore box may not exist
2021-04-24 17:49:26 +02:00
rect ( ctx , result [ i ] . box [ 0 ] , result [ i ] . box [ 1 ] , result [ i ] . box [ 2 ] , result [ i ] . box [ 3 ] , localOptions ) ;
if ( localOptions . drawLabels ) {
if ( localOptions . shadowColor && localOptions . shadowColor !== '' ) {
ctx . fillStyle = localOptions . shadowColor ;
2021-05-22 18:33:19 +02:00
// @ts-ignore box may not exist
2021-04-24 17:49:26 +02:00
ctx . fillText ( ` body ${ 100 * result [ i ] . score } % ` , result [ i ] . box [ 0 ] + 3 , 1 + result [ i ] . box [ 1 ] + localOptions . lineHeight , result [ i ] . box [ 2 ] ) ;
}
ctx . fillStyle = localOptions . labelColor ;
2021-05-22 18:33:19 +02:00
// @ts-ignore box may not exist
2021-04-24 17:49:26 +02:00
ctx . fillText ( ` body ${ 100 * result [ i ] . score } % ` , result [ i ] . box [ 0 ] + 2 , 0 + result [ i ] . box [ 1 ] + localOptions . lineHeight , result [ i ] . box [ 2 ] ) ;
}
}
2021-04-13 17:05:52 +02:00
if ( localOptions . drawPoints ) {
2021-03-05 17:43:50 +01:00
for ( let pt = 0 ; pt < result [ i ] . keypoints . length ; pt ++ ) {
2021-05-22 20:53:51 +02:00
ctx . fillStyle = localOptions . useDepth && result [ i ] . keypoints [ pt ] . position . z ? ` rgba( ${ 127.5 + ( 2 * ( result [ i ] . keypoints [ pt ] . position . z || 0 ) ) } , ${ 127.5 - ( 2 * ( result [ i ] . keypoints [ pt ] . position . z || 0 ) ) } , 255, 0.5) ` : localOptions . color ;
2021-05-22 19:17:07 +02:00
point ( ctx , result [ i ] . keypoints [ pt ] . position . x , result [ i ] . keypoints [ pt ] . position . y , 0 , localOptions ) ;
2021-03-05 20:30:09 +01:00
}
}
2021-04-13 17:05:52 +02:00
if ( localOptions . drawLabels ) {
ctx . font = localOptions . font ;
2021-03-26 23:50:19 +01:00
if ( result [ i ] . keypoints ) {
for ( const pt of result [ i ] . keypoints ) {
2021-04-13 17:05:52 +02:00
ctx . fillStyle = localOptions . useDepth && pt . position . z ? ` rgba( ${ 127.5 + ( 2 * pt . position . z ) } , ${ 127.5 - ( 2 * pt . position . z ) } , 255, 0.5) ` : localOptions . color ;
2021-04-24 17:49:26 +02:00
ctx . fillText ( ` ${ pt . part } ${ Math . trunc ( 100 * pt . score ) } % ` , pt . position . x + 4 , pt . position . y + 4 ) ;
2021-03-26 23:50:19 +01:00
}
2021-03-05 17:43:50 +01:00
}
}
2021-04-13 17:05:52 +02:00
if ( localOptions . drawPolygons && result [ i ] . keypoints ) {
2021-03-05 17:43:50 +01:00
let part ;
2021-05-22 20:53:51 +02:00
const points : [ number , number , number ? ] [ ] = [ ] ;
2021-03-28 19:22:22 +02:00
// shoulder line
2021-03-06 16:38:04 +01:00
points . length = 0 ;
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftShoulder' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightShoulder' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-04-13 17:05:52 +02:00
curves ( ctx , points , localOptions ) ;
2021-03-28 19:22:22 +02:00
// torso main
points . length = 0 ;
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightShoulder' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightHip' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftHip' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftShoulder' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-04-13 17:05:52 +02:00
if ( points . length === 4 ) lines ( ctx , points , localOptions ) ; // only draw if we have complete torso
2021-03-05 17:43:50 +01:00
// leg left
2021-03-06 16:38:04 +01:00
points . length = 0 ;
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftHip' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftKnee' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftAnkle' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftHeel' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftFoot' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-04-13 17:05:52 +02:00
curves ( ctx , points , localOptions ) ;
2021-03-05 17:43:50 +01:00
// leg right
2021-03-06 16:38:04 +01:00
points . length = 0 ;
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightHip' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightKnee' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightAnkle' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightHeel' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightFoot' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-04-13 17:05:52 +02:00
curves ( ctx , points , localOptions ) ;
2021-03-05 17:43:50 +01:00
// arm left
2021-03-06 16:38:04 +01:00
points . length = 0 ;
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftShoulder' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftElbow' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftWrist' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'leftPalm' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-04-13 17:05:52 +02:00
curves ( ctx , points , localOptions ) ;
2021-03-05 17:43:50 +01:00
// arm right
2021-03-06 16:38:04 +01:00
points . length = 0 ;
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightShoulder' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightElbow' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightWrist' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-03-06 16:38:04 +01:00
part = result [ i ] . keypoints . find ( ( a ) = > a . part === 'rightPalm' ) ;
2021-05-05 02:46:33 +02:00
if ( part ) points . push ( [ part . position . x , part . position . y ] ) ;
2021-04-13 17:05:52 +02:00
curves ( ctx , points , localOptions ) ;
2021-03-05 17:43:50 +01:00
// draw all
}
}
}
2021-05-22 18:33:19 +02:00
export async function hand ( inCanvas : HTMLCanvasElement , result : Array < Hand > , drawOptions? : DrawOptions ) {
2021-04-13 17:05:52 +02:00
const localOptions = mergeDeep ( options , drawOptions ) ;
2021-03-05 17:43:50 +01:00
if ( ! result || ! inCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) ) return ;
const ctx = inCanvas . getContext ( '2d' ) ;
if ( ! ctx ) return ;
ctx . lineJoin = 'round' ;
2021-04-13 17:05:52 +02:00
ctx . font = localOptions . font ;
2021-03-05 17:43:50 +01:00
for ( const h of result ) {
2021-04-13 17:05:52 +02:00
if ( localOptions . drawBoxes ) {
ctx . strokeStyle = localOptions . color ;
ctx . fillStyle = localOptions . color ;
2021-05-25 14:58:20 +02:00
rect ( ctx , h . box [ 0 ] , h . box [ 1 ] , h . box [ 2 ] , h . box [ 3 ] , localOptions ) ;
2021-04-13 17:05:52 +02:00
if ( localOptions . drawLabels ) {
if ( localOptions . shadowColor && localOptions . shadowColor !== '' ) {
ctx . fillStyle = localOptions . shadowColor ;
2021-05-25 14:58:20 +02:00
ctx . fillText ( 'hand' , h . box [ 0 ] + 3 , 1 + h . box [ 1 ] + localOptions . lineHeight , h . box [ 2 ] ) ;
2021-03-17 16:32:37 +01:00
}
2021-04-13 17:05:52 +02:00
ctx . fillStyle = localOptions . labelColor ;
2021-05-25 14:58:20 +02:00
ctx . fillText ( 'hand' , h . box [ 0 ] + 2 , 0 + h . box [ 1 ] + localOptions . lineHeight , h . box [ 2 ] ) ;
2021-03-05 17:43:50 +01:00
}
ctx . stroke ( ) ;
}
2021-04-13 17:05:52 +02:00
if ( localOptions . drawPoints ) {
2021-03-05 17:43:50 +01:00
if ( h . landmarks && h . landmarks . length > 0 ) {
for ( const pt of h . landmarks ) {
2021-04-13 17:05:52 +02:00
ctx . fillStyle = localOptions . useDepth ? ` rgba( ${ 127.5 + ( 2 * pt [ 2 ] ) } , ${ 127.5 - ( 2 * pt [ 2 ] ) } , 255, 0.5) ` : localOptions . color ;
point ( ctx , pt [ 0 ] , pt [ 1 ] , 0 , localOptions ) ;
2021-03-05 17:43:50 +01:00
}
}
}
2021-04-26 13:37:29 +02:00
if ( localOptions . drawLabels ) {
const addHandLabel = ( part , title ) = > {
ctx . fillStyle = localOptions . useDepth ? ` rgba( ${ 127.5 + ( 2 * part [ part . length - 1 ] [ 2 ] ) } , ${ 127.5 - ( 2 * part [ part . length - 1 ] [ 2 ] ) } , 255, 0.5) ` : localOptions . color ;
ctx . fillText ( title , part [ part . length - 1 ] [ 0 ] + 4 , part [ part . length - 1 ] [ 1 ] + 4 ) ;
} ;
ctx . font = localOptions . font ;
2021-05-22 18:33:19 +02:00
addHandLabel ( h . annotations [ 'indexFinger' ] , 'index' ) ;
addHandLabel ( h . annotations [ 'middleFinger' ] , 'middle' ) ;
addHandLabel ( h . annotations [ 'ringFinger' ] , 'ring' ) ;
addHandLabel ( h . annotations [ 'pinky' ] , 'pinky' ) ;
addHandLabel ( h . annotations [ 'thumb' ] , 'thumb' ) ;
addHandLabel ( h . annotations [ 'palmBase' ] , 'palm' ) ;
2021-04-26 13:37:29 +02:00
}
2021-04-13 17:05:52 +02:00
if ( localOptions . drawPolygons ) {
2021-04-26 13:37:29 +02:00
const addHandLine = ( part ) = > {
2021-03-05 17:43:50 +01:00
if ( ! part ) return ;
for ( let i = 0 ; i < part . length ; i ++ ) {
ctx . beginPath ( ) ;
2021-04-13 17:05:52 +02:00
ctx . strokeStyle = localOptions . useDepth ? ` rgba( ${ 127.5 + ( 2 * part [ i ] [ 2 ] ) } , ${ 127.5 - ( 2 * part [ i ] [ 2 ] ) } , 255, 0.5) ` : localOptions . color ;
2021-03-05 17:43:50 +01:00
ctx . moveTo ( part [ i > 0 ? i - 1 : 0 ] [ 0 ] , part [ i > 0 ? i - 1 : 0 ] [ 1 ] ) ;
ctx . lineTo ( part [ i ] [ 0 ] , part [ i ] [ 1 ] ) ;
ctx . stroke ( ) ;
}
} ;
2021-04-26 13:37:29 +02:00
ctx . lineWidth = localOptions . lineWidth ;
2021-05-22 18:33:19 +02:00
addHandLine ( h . annotations [ 'indexFinger' ] ) ;
addHandLine ( h . annotations [ 'middleFinger' ] ) ;
addHandLine ( h . annotations [ 'ringFinger' ] ) ;
addHandLine ( h . annotations [ 'pinky' ] ) ;
addHandLine ( h . annotations [ 'thumb' ] ) ;
2021-04-26 13:37:29 +02:00
// addPart(h.annotations.palmBase);
2021-03-05 17:43:50 +01:00
}
}
}
2021-05-22 18:33:19 +02:00
export async function object ( inCanvas : HTMLCanvasElement , result : Array < Item > , drawOptions? : DrawOptions ) {
2021-04-13 17:05:52 +02:00
const localOptions = mergeDeep ( options , drawOptions ) ;
2021-03-17 16:32:37 +01:00
if ( ! result || ! inCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) ) return ;
const ctx = inCanvas . getContext ( '2d' ) ;
if ( ! ctx ) return ;
ctx . lineJoin = 'round' ;
2021-04-13 17:05:52 +02:00
ctx . font = localOptions . font ;
2021-03-17 16:32:37 +01:00
for ( const h of result ) {
2021-04-13 17:05:52 +02:00
if ( localOptions . drawBoxes ) {
ctx . strokeStyle = localOptions . color ;
ctx . fillStyle = localOptions . color ;
2021-05-25 14:58:20 +02:00
rect ( ctx , h . box [ 0 ] , h . box [ 1 ] , h . box [ 2 ] , h . box [ 3 ] , localOptions ) ;
2021-04-13 17:05:52 +02:00
if ( localOptions . drawLabels ) {
2021-03-17 16:32:37 +01:00
const label = ` ${ Math . round ( 100 * h . score ) } % ${ h . label } ` ;
2021-04-13 17:05:52 +02:00
if ( localOptions . shadowColor && localOptions . shadowColor !== '' ) {
ctx . fillStyle = localOptions . shadowColor ;
ctx . fillText ( label , h . box [ 0 ] + 3 , 1 + h . box [ 1 ] + localOptions . lineHeight , h . box [ 2 ] ) ;
2021-03-17 16:32:37 +01:00
}
2021-04-13 17:05:52 +02:00
ctx . fillStyle = localOptions . labelColor ;
ctx . fillText ( label , h . box [ 0 ] + 2 , 0 + h . box [ 1 ] + localOptions . lineHeight , h . box [ 2 ] ) ;
2021-03-17 16:32:37 +01:00
}
ctx . stroke ( ) ;
}
}
}
2021-05-24 17:10:13 +02:00
export async function person ( inCanvas : HTMLCanvasElement , result : Array < Person > , drawOptions? : DrawOptions ) {
const localOptions = mergeDeep ( options , drawOptions ) ;
if ( ! result || ! inCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) ) return ;
const ctx = inCanvas . getContext ( '2d' ) ;
if ( ! ctx ) return ;
ctx . lineJoin = 'round' ;
ctx . font = localOptions . font ;
2021-05-25 14:58:20 +02:00
2021-05-24 17:10:13 +02:00
for ( let i = 0 ; i < result . length ; i ++ ) {
if ( localOptions . drawBoxes ) {
ctx . strokeStyle = localOptions . color ;
ctx . fillStyle = localOptions . color ;
rect ( ctx , result [ i ] . box [ 0 ] , result [ i ] . box [ 1 ] , result [ i ] . box [ 2 ] , result [ i ] . box [ 3 ] , localOptions ) ;
if ( localOptions . drawLabels ) {
const label = ` person # ${ i } ` ;
if ( localOptions . shadowColor && localOptions . shadowColor !== '' ) {
ctx . fillStyle = localOptions . shadowColor ;
ctx . fillText ( label , result [ i ] . box [ 0 ] + 3 , 1 + result [ i ] . box [ 1 ] + localOptions . lineHeight , result [ i ] . box [ 2 ] ) ;
}
ctx . fillStyle = localOptions . labelColor ;
ctx . fillText ( label , result [ i ] . box [ 0 ] + 2 , 0 + result [ i ] . box [ 1 ] + localOptions . lineHeight , result [ i ] . box [ 2 ] ) ;
}
ctx . stroke ( ) ;
}
}
}
2021-05-23 19:52:49 +02:00
function calcBuffered ( newResult , localOptions ) {
2021-05-30 18:03:34 +02:00
// each record is only updated using deep clone when number of detected record changes, otherwise it will converge by itself
// otherwise bufferedResult is a shallow clone of result plus updated local calculated values
// thus mixing by-reference and by-value assignments to minimize memory operations
2021-05-23 19:52:49 +02:00
2021-05-25 14:58:20 +02:00
// interpolate body results
2021-05-30 18:03:34 +02:00
if ( ! bufferedResult . body || ( newResult . body . length !== bufferedResult . body . length ) ) bufferedResult . body = JSON . parse ( JSON . stringify ( newResult . body ) ) ; // deep clone once
for ( let i = 0 ; i < newResult . body . length ; i ++ ) {
const box = newResult . body [ i ] . box // update box
. map ( ( b , j ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . body [ i ] . box [ j ] + b ) / localOptions . bufferedFactor ) as [ number , number , number , number ] ;
const boxRaw = newResult . body [ i ] . boxRaw // update boxRaw
. map ( ( b , j ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . body [ i ] . boxRaw [ j ] + b ) / localOptions . bufferedFactor ) as [ number , number , number , number ] ;
const keypoints = newResult . body [ i ] . keypoints // update keypoints
2021-05-23 19:52:49 +02:00
. map ( ( keypoint , j ) = > ( {
score : keypoint.score ,
part : keypoint.part ,
position : {
x : bufferedResult.body [ i ] . keypoints [ j ] ? ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . body [ i ] . keypoints [ j ] . position . x + keypoint . position . x ) / localOptions.bufferedFactor : keypoint.position.x ,
y : bufferedResult.body [ i ] . keypoints [ j ] ? ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . body [ i ] . keypoints [ j ] . position . y + keypoint . position . y ) / localOptions.bufferedFactor : keypoint.position.y ,
} ,
} ) ) ;
2021-05-30 18:03:34 +02:00
bufferedResult . body [ i ] = { . . . newResult . body [ i ] , box , boxRaw , keypoints } ; // shallow clone plus updated values
2021-05-23 19:52:49 +02:00
}
2021-05-25 14:58:20 +02:00
// interpolate hand results
2021-05-30 18:03:34 +02:00
if ( ! bufferedResult . hand || ( newResult . hand . length !== bufferedResult . hand . length ) ) bufferedResult . hand = JSON . parse ( JSON . stringify ( newResult . hand ) ) ; // deep clone once
for ( let i = 0 ; i < newResult . hand . length ; i ++ ) {
const box = newResult . hand [ i ] . box // update box
. map ( ( b , j ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . hand [ i ] . box [ j ] + b ) / localOptions . bufferedFactor ) ;
const boxRaw = newResult . hand [ i ] . boxRaw // update boxRaw
. map ( ( b , j ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . hand [ i ] . boxRaw [ j ] + b ) / localOptions . bufferedFactor ) ;
const landmarks = newResult . hand [ i ] . landmarks // update landmarks
2021-05-23 19:52:49 +02:00
. map ( ( landmark , j ) = > landmark
. map ( ( coord , k ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . hand [ i ] . landmarks [ j ] [ k ] + coord ) / localOptions . bufferedFactor ) ) ;
2021-05-30 18:03:34 +02:00
const keys = Object . keys ( newResult . hand [ i ] . annotations ) ; // update annotations
const annotations = [ ] ;
2021-05-23 19:52:49 +02:00
for ( const key of keys ) {
2021-05-30 18:03:34 +02:00
annotations [ key ] = newResult . hand [ i ] . annotations [ key ]
2021-05-23 19:52:49 +02:00
. map ( ( val , j ) = > val
. map ( ( coord , k ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . hand [ i ] . annotations [ key ] [ j ] [ k ] + coord ) / localOptions . bufferedFactor ) ) ;
}
2021-05-30 18:03:34 +02:00
bufferedResult . hand [ i ] = { . . . newResult . hand [ i ] , box , boxRaw , landmarks , annotations } ; // shallow clone plus updated values
}
// interpolate face results
if ( ! bufferedResult . face || ( newResult . face . length !== bufferedResult . face . length ) ) bufferedResult . face = JSON . parse ( JSON . stringify ( newResult . face ) ) ; // deep clone once
for ( let i = 0 ; i < newResult . face . length ; i ++ ) {
const box = newResult . face [ i ] . box // update box
. map ( ( b , j ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . face [ i ] . box [ j ] + b ) / localOptions . bufferedFactor ) ;
const boxRaw = newResult . face [ i ] . boxRaw // update boxRaw
. map ( ( b , j ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . face [ i ] . boxRaw [ j ] + b ) / localOptions . bufferedFactor ) ;
bufferedResult . face [ i ] = { . . . newResult . face [ i ] , box , boxRaw } ; // shallow clone plus updated values
2021-05-23 19:52:49 +02:00
}
2021-05-25 14:58:20 +02:00
// interpolate person results
const newPersons = newResult . persons ; // trigger getter function
if ( ! bufferedResult . persons || ( newPersons . length !== bufferedResult . persons . length ) ) bufferedResult . persons = JSON . parse ( JSON . stringify ( newPersons ) ) ;
for ( let i = 0 ; i < newPersons . length ; i ++ ) { // update person box, we don't update the rest as it's updated as reference anyhow
bufferedResult . persons [ i ] . box = newPersons [ i ] . box
. map ( ( box , j ) = > ( ( localOptions . bufferedFactor - 1 ) * bufferedResult . persons [ i ] . box [ j ] + box ) / localOptions . bufferedFactor ) ;
}
2021-05-23 19:52:49 +02:00
// no buffering implemented for face, object, gesture
2021-05-24 17:10:13 +02:00
// bufferedResult.face = JSON.parse(JSON.stringify(newResult.face));
// bufferedResult.object = JSON.parse(JSON.stringify(newResult.object));
// bufferedResult.gesture = JSON.parse(JSON.stringify(newResult.gesture));
2021-05-23 19:52:49 +02:00
}
2021-03-17 23:23:19 +01:00
export async function canvas ( inCanvas : HTMLCanvasElement , outCanvas : HTMLCanvasElement ) {
2021-03-05 17:43:50 +01:00
if ( ! inCanvas || ! outCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) || ! ( outCanvas instanceof HTMLCanvasElement ) ) return ;
const outCtx = inCanvas . getContext ( '2d' ) ;
outCtx ? . drawImage ( inCanvas , 0 , 0 ) ;
}
2021-05-22 18:33:19 +02:00
export async function all ( inCanvas : HTMLCanvasElement , result : Result , drawOptions? : DrawOptions ) {
2021-04-13 17:05:52 +02:00
const localOptions = mergeDeep ( options , drawOptions ) ;
2021-03-05 17:43:50 +01:00
if ( ! result || ! inCanvas ) return ;
if ( ! ( inCanvas instanceof HTMLCanvasElement ) ) return ;
2021-05-25 14:58:20 +02:00
if ( localOptions . bufferedOutput ) calcBuffered ( result , localOptions ) ; // do results interpolation
else bufferedResult = result ; // just use results as-is
2021-05-30 18:03:34 +02:00
face ( inCanvas , bufferedResult . face , localOptions ) ; // face does have buffering
2021-05-24 17:10:13 +02:00
body ( inCanvas , bufferedResult . body , localOptions ) ; // use interpolated results if available
hand ( inCanvas , bufferedResult . hand , localOptions ) ; // use interpolated results if available
2021-05-25 14:58:20 +02:00
// person(inCanvas, bufferedResult.persons, localOptions); // use interpolated results if available
2021-05-24 17:10:13 +02:00
gesture ( inCanvas , result . gesture , localOptions ) ; // gestures do not have buffering
object ( inCanvas , result . object , localOptions ) ; // object detection does not have buffering
2021-03-05 17:43:50 +01:00
}