@@ -21,7 +21,7 @@ import {isMobile} from './util';
21
21
22
22
// These anchor points allow the hand pointcloud to resize according to its
23
23
// position in the input.
24
- const ANCHOR_POINTS = [ [ 0 , 0 , 0 ] , [ 0 , 1 , 0 ] , [ - 1 , 0 , 0 ] , [ - 1 , - 1 , 0 ] ] ;
24
+ const ANCHOR_POINTS = [ [ 0 , 0 , 0 ] , [ 0 , 0. 1, 0 ] , [ - 0. 1, 0 , 0 ] , [ - 0. 1, - 0. 1, 0 ] ] ;
25
25
26
26
const fingerLookupIndices = {
27
27
thumb : [ 0 , 1 , 2 , 3 , 4 ] ,
@@ -39,18 +39,27 @@ const connections = [
39
39
[ 0 , 17 ] , [ 17 , 18 ] , [ 18 , 19 ] , [ 19 , 20 ]
40
40
] ;
41
41
42
+ function createScatterGLContext ( selectors ) {
43
+ const scatterGLEl = document . querySelector ( selectors ) ;
44
+ return {
45
+ scatterGLEl,
46
+ scatterGL : new scatter . ScatterGL ( scatterGLEl , {
47
+ 'rotateOnStart' : true ,
48
+ 'selectEnabled' : false ,
49
+ 'styles' : { polyline : { defaultOpacity : 1 , deselectedOpacity : 1 } }
50
+ } ) ,
51
+ scatterGLHasInitialized : false ,
52
+ } ;
53
+ }
54
+
55
+ const scatterGLCtxtLeftHand = createScatterGLContext ( '#scatter-gl-container-left' ) ;
56
+ const scatterGLCtxtRightHand = createScatterGLContext ( '#scatter-gl-container-right' ) ;
57
+
42
58
export class Camera {
43
59
constructor ( ) {
44
60
this . video = document . getElementById ( 'video' ) ;
45
61
this . canvas = document . getElementById ( 'output' ) ;
46
62
this . ctx = this . canvas . getContext ( '2d' ) ;
47
- this . scatterGLEl = document . querySelector ( '#scatter-gl-container' ) ;
48
- this . scatterGL = new scatter . ScatterGL ( this . scatterGLEl , {
49
- 'rotateOnStart' : true ,
50
- 'selectEnabled' : false ,
51
- 'styles' : { polyline : { defaultOpacity : 1 , deselectedOpacity : 1 } }
52
- } ) ;
53
- this . scatterGLHasInitialized = false ;
54
63
}
55
64
56
65
/**
@@ -108,12 +117,14 @@ export class Camera {
108
117
camera . ctx . translate ( camera . video . videoWidth , 0 ) ;
109
118
camera . ctx . scale ( - 1 , 1 ) ;
110
119
111
- camera . scatterGLEl . style =
112
- `width: ${ videoWidth } px; height: ${ videoHeight } px;` ;
113
- camera . scatterGL . resize ( ) ;
120
+ for ( const ctxt of [ scatterGLCtxtLeftHand , scatterGLCtxtRightHand ] ) {
121
+ ctxt . scatterGLEl . style =
122
+ `width: ${ videoWidth / 2 } px; height: ${ videoHeight / 2 } px;` ;
123
+ ctxt . scatterGL . resize ( ) ;
114
124
115
- camera . scatterGLEl . style . display =
116
- params . STATE . modelConfig . render3D ? 'inline-block' : 'none' ;
125
+ ctxt . scatterGLEl . style . display =
126
+ params . STATE . modelConfig . render3D ? 'inline-block' : 'none' ;
127
+ }
117
128
118
129
return camera ;
119
130
}
@@ -132,31 +143,53 @@ export class Camera {
132
143
* @param hands A list of hands to render.
133
144
*/
134
145
drawResults ( hands ) {
135
- for ( const hand of hands ) {
136
- this . drawResult ( hand ) ;
146
+ // Sort by right to left hands.
147
+ hands . sort ( ( hand1 , hand2 ) => {
148
+ if ( hand1 . handedness < hand2 . handedness ) return 1 ;
149
+ if ( hand1 . handedness > hand2 . handedness ) return - 1 ;
150
+ return 0 ;
151
+ } ) ;
152
+
153
+ // Pad hands to clear empty scatter GL plots.
154
+ while ( hands . length < 2 ) hands . push ( { } ) ;
155
+
156
+ for ( let i = 0 ; i < hands . length ; ++ i ) {
157
+ // Third hand and onwards scatterGL context is set to null since we
158
+ // don't render them.
159
+ const ctxt = [ scatterGLCtxtLeftHand , scatterGLCtxtRightHand ] [ i ] ;
160
+ this . drawResult ( hands [ i ] , ctxt ) ;
137
161
}
138
162
}
139
163
140
164
/**
141
165
* Draw the keypoints on the video.
142
166
* @param hand A hand with keypoints to render.
167
+ * @param ctxt Scatter GL context to render 3D keypoints to.
143
168
*/
144
- drawResult ( hand ) {
169
+ drawResult ( hand , ctxt ) {
145
170
if ( hand . keypoints != null ) {
146
- this . drawKeypoints ( hand . keypoints ) ;
171
+ this . drawKeypoints ( hand . keypoints , hand . handedness ) ;
172
+ }
173
+ // Don't render 3D hands after first two.
174
+ if ( ctxt == null ) {
175
+ return ;
147
176
}
148
177
if ( hand . keypoints3D != null && params . STATE . modelConfig . render3D ) {
149
- this . drawKeypoints3D ( hand . keypoints3D ) ;
178
+ this . drawKeypoints3D ( hand . keypoints3D , hand . handedness , ctxt ) ;
179
+ } else {
180
+ // Clear scatter plot.
181
+ this . drawKeypoints3D ( [ ] , '' , ctxt ) ;
150
182
}
151
183
}
152
184
153
185
/**
154
186
* Draw the keypoints on the video.
155
187
* @param keypoints A list of keypoints.
188
+ * @param handedness Label of hand (either Left or Right).
156
189
*/
157
- drawKeypoints ( keypoints ) {
190
+ drawKeypoints ( keypoints , handedness ) {
158
191
const keypointsArray = keypoints ;
159
- this . ctx . fillStyle = ' Red';
192
+ this . ctx . fillStyle = handedness === 'Left' ? ' Red' : 'Blue ';
160
193
this . ctx . strokeStyle = 'White' ;
161
194
this . ctx . lineWidth = params . DEFAULT_LINE_WIDTH ;
162
195
@@ -194,29 +227,29 @@ export class Camera {
194
227
this . ctx . fill ( ) ;
195
228
}
196
229
197
- drawKeypoints3D ( keypoints ) {
230
+ drawKeypoints3D ( keypoints , handedness , ctxt ) {
198
231
const scoreThreshold = params . STATE . modelConfig . scoreThreshold || 0 ;
199
232
const pointsData =
200
233
keypoints . map ( keypoint => ( [ - keypoint . x , - keypoint . y , - keypoint . z ] ) ) ;
201
234
202
235
const dataset =
203
236
new scatter . ScatterGL . Dataset ( [ ...pointsData , ...ANCHOR_POINTS ] ) ;
204
237
205
- this . scatterGL . setPointColorer ( ( i ) => {
238
+ ctxt . scatterGL . setPointColorer ( ( i ) => {
206
239
if ( keypoints [ i ] == null || keypoints [ i ] . score < scoreThreshold ) {
207
240
// hide anchor points and low-confident points.
208
241
return '#ffffff' ;
209
242
}
210
- return ' #ff0000' /* Red */ ;
243
+ return handedness === 'Left' ? ' #ff0000' : '#0000ff' ;
211
244
} ) ;
212
245
213
- if ( ! this . scatterGLHasInitialized ) {
214
- this . scatterGL . render ( dataset ) ;
246
+ if ( ! ctxt . scatterGLHasInitialized ) {
247
+ ctxt . scatterGL . render ( dataset ) ;
215
248
} else {
216
- this . scatterGL . updateDataset ( dataset ) ;
249
+ ctxt . scatterGL . updateDataset ( dataset ) ;
217
250
}
218
251
const sequences = connections . map ( pair => ( { indices : pair } ) ) ;
219
- this . scatterGL . setSequences ( sequences ) ;
220
- this . scatterGLHasInitialized = true ;
252
+ ctxt . scatterGL . setSequences ( sequences ) ;
253
+ ctxt . scatterGLHasInitialized = true ;
221
254
}
222
255
}
0 commit comments