1
+ /**
2
+ * @license
3
+ * Copyright 2021 Google LLC. All Rights Reserved.
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ * =============================================================================
16
+ */
17
+ import { Keypoint , Pose } from '../types' ;
18
+ import { KeypointTracker } from './keypoint_tracker' ;
19
+ import { Track } from './interfaces/common_interfaces' ;
20
+ import { TrackerConfig } from './interfaces/config_interfaces' ;
21
+
22
+ describe ( 'Keypoint tracker' , ( ) => {
23
+ const trackerConfig : TrackerConfig = {
24
+ maxTracks : 4 ,
25
+ maxAge : 1000 ,
26
+ minSimilarity : 0.5 ,
27
+ trackerParams : {
28
+ keypointConfidenceThreshold : 0.2 ,
29
+ keypointFalloff : [ 0.1 , 0.1 , 0.1 , 0.1 ] ,
30
+ minNumberOfKeypoints : 2
31
+ }
32
+ } ;
33
+
34
+ it ( 'Instantiate tracker' , ( ) => {
35
+ const kptTracker = new KeypointTracker ( trackerConfig ) ;
36
+ expect ( kptTracker instanceof KeypointTracker ) . toBe ( true ) ;
37
+ } ) ;
38
+
39
+ it ( 'Compute OKS' , ( ) => {
40
+ const kptTracker = new KeypointTracker ( trackerConfig ) ;
41
+ const pose : Pose = {
42
+ keypoints : [
43
+ { x : 0.2 , y : 0.2 , score : 1.0 } ,
44
+ { x : 0.4 , y : 0.4 , score : 0.8 } ,
45
+ { x : 0.6 , y : 0.6 , score : 0.1 } , // Low confidence.
46
+ { x : 0.8 , y : 0.7 , score : 0.8 }
47
+ ] } ;
48
+ const track : Track = {
49
+ id : 0 ,
50
+ lastTimestamp : 1000 ,
51
+ keypoints : [
52
+ { x : 0.2 , y : 0.2 , score : 1.0 } ,
53
+ { x : 0.4 , y : 0.4 , score : 0.8 } ,
54
+ { x : 0.6 , y : 0.6 , score : 0.9 } ,
55
+ { x : 0.8 , y : 0.8 , score : 0.8 }
56
+ ] } ;
57
+ const oks = kptTracker [ 'oks' ] ( pose , track ) ;
58
+
59
+ const boxArea = ( 0.8 - 0.2 ) * ( 0.8 - 0.2 ) ;
60
+ const x = 2 * trackerConfig . trackerParams . keypointFalloff [ 3 ] ;
61
+ const d = 0.1 ;
62
+ const expectedOks =
63
+ ( 1 + 1 + Math . exp ( - 1 * d ** 2 / ( 2 * boxArea * x ** 2 ) ) ) / 3 ;
64
+ expect ( oks ) . toBeCloseTo ( expectedOks , 6 ) ;
65
+ } ) ;
66
+
67
+ it ( 'Compute OKS returns 0.0 with less than 2 valid keypoints' , ( ) => {
68
+ const kptTracker = new KeypointTracker ( trackerConfig ) ;
69
+ const pose : Pose = {
70
+ keypoints : [
71
+ { x : 0.2 , y : 0.2 , score : 1.0 } ,
72
+ { x : 0.4 , y : 0.4 , score : 0.1 } , // Low confidence.
73
+ { x : 0.6 , y : 0.6 , score : 0.9 } ,
74
+ { x : 0.8 , y : 0.8 , score : 0.8 }
75
+ ] } ;
76
+ const track : Track = {
77
+ id : 0 ,
78
+ lastTimestamp : 1000 ,
79
+ keypoints : [
80
+ { x : 0.2 , y : 0.2 , score : 1.0 } ,
81
+ { x : 0.4 , y : 0.4 , score : 0.8 } ,
82
+ { x : 0.6 , y : 0.6 , score : 0.1 } , // Low confidence.
83
+ { x : 0.8 , y : 0.8 , score : 0.0 } // Low confidence.
84
+ ] } ;
85
+ const oks = kptTracker [ 'oks' ] ( pose , track ) ;
86
+ expect ( oks ) . toBeCloseTo ( 0.0 , 6 ) ;
87
+ } ) ;
88
+
89
+ it ( 'Compute area' , ( ) => {
90
+ const kptTracker = new KeypointTracker ( trackerConfig ) ;
91
+ const keypoints : Keypoint [ ] = [
92
+ { x : 0.1 , y : 0.2 , score : 1.0 } ,
93
+ { x : 0.3 , y : 0.4 , score : 0.9 } ,
94
+ { x : 0.4 , y : 0.6 , score : 0.9 } ,
95
+ { x : 0.7 , y : 0.8 , score : 0.1 } // Low confidence.
96
+ ] ;
97
+ const area = kptTracker [ 'area' ] ( keypoints ) ;
98
+
99
+ const expectedArea = ( 0.4 - 0.1 ) * ( 0.6 - 0.2 ) ;
100
+ expect ( area ) . toBeCloseTo ( expectedArea , 6 ) ;
101
+ } ) ;
102
+
103
+ it ( 'Apply tracker' , ( ) => {
104
+ // Timestamp: 0. Pose becomes the only track.
105
+ const kptTracker = new KeypointTracker ( trackerConfig ) ;
106
+ let tracks : Track [ ] ;
107
+ let poses : Pose [ ] = [
108
+ { keypoints : [ // Becomes id = 1.
109
+ { x : 0.2 , y : 0.2 , score : 1.0 } ,
110
+ { x : 0.4 , y : 0.4 , score : 0.8 } ,
111
+ { x : 0.6 , y : 0.6 , score : 0.9 } ,
112
+ { x : 0.8 , y : 0.8 , score : 0.0 } // Low confidence.
113
+ ] }
114
+ ] ;
115
+ poses = kptTracker . apply ( poses , 0 ) ;
116
+ tracks = kptTracker . getTracks ( ) ;
117
+ expect ( poses . length ) . toEqual ( 1 ) ;
118
+ expect ( poses [ 0 ] . id ) . toEqual ( 1 ) ;
119
+ expect ( tracks . length ) . toEqual ( 1 ) ;
120
+ expect ( tracks [ 0 ] . id ) . toEqual ( 1 ) ;
121
+ expect ( tracks [ 0 ] . lastTimestamp ) . toEqual ( 0 ) ;
122
+
123
+ // Timestamp: 100. First pose is linked with track 1. Second pose spawns a
124
+ // new track (id = 2).
125
+ poses = [
126
+ { keypoints : [ // Links with id = 1.
127
+ { x : 0.2 , y : 0.2 , score : 1.0 } ,
128
+ { x : 0.4 , y : 0.4 , score : 0.8 } ,
129
+ { x : 0.6 , y : 0.6 , score : 0.9 } ,
130
+ { x : 0.8 , y : 0.8 , score : 0.8 }
131
+ ] } ,
132
+ { keypoints : [ // Becomes id = 2.
133
+ { x : 0.8 , y : 0.8 , score : 0.8 } ,
134
+ { x : 0.6 , y : 0.6 , score : 0.3 } ,
135
+ { x : 0.4 , y : 0.4 , score : 0.1 } , // Low confidence.
136
+ { x : 0.2 , y : 0.2 , score : 0.8 }
137
+ ] }
138
+ ] ;
139
+ poses = kptTracker . apply ( poses , 100 ) ;
140
+ tracks = kptTracker . getTracks ( ) ;
141
+ expect ( poses . length ) . toEqual ( 2 ) ;
142
+ expect ( poses [ 0 ] . id ) . toEqual ( 1 ) ;
143
+ expect ( poses [ 1 ] . id ) . toEqual ( 2 ) ;
144
+ expect ( tracks . length ) . toEqual ( 2 ) ;
145
+ expect ( tracks [ 0 ] . id ) . toEqual ( 1 ) ;
146
+ expect ( tracks [ 0 ] . lastTimestamp ) . toEqual ( 100 ) ;
147
+ expect ( tracks [ 1 ] . id ) . toEqual ( 2 ) ;
148
+ expect ( tracks [ 1 ] . lastTimestamp ) . toEqual ( 100 ) ;
149
+
150
+ // Timestamp: 900. First pose is linked with track 2. Second pose spawns a
151
+ // new track (id = 3).
152
+ poses = [
153
+ { keypoints : [ // Links with id = 2.
154
+ { x : 0.6 , y : 0.7 , score : 0.7 } ,
155
+ { x : 0.5 , y : 0.6 , score : 0.7 } ,
156
+ { x : 0.0 , y : 0.0 , score : 0.1 } , // Low confidence.
157
+ { x : 0.2 , y : 0.1 , score : 1.0 }
158
+ ] } ,
159
+ { keypoints : [ // Becomes id = 3.
160
+ { x : 0.5 , y : 0.1 , score : 0.6 } ,
161
+ { x : 0.9 , y : 0.3 , score : 0.6 } ,
162
+ { x : 0.1 , y : 1.0 , score : 0.9 } ,
163
+ { x : 0.4 , y : 0.4 , score : 0.1 } // Low confidence.
164
+ ] } ,
165
+ ] ;
166
+ poses = kptTracker . apply ( poses , 900 ) ;
167
+ tracks = kptTracker . getTracks ( ) ;
168
+ expect ( poses . length ) . toEqual ( 2 ) ;
169
+ expect ( poses [ 0 ] . id ) . toEqual ( 2 ) ;
170
+ expect ( poses [ 1 ] . id ) . toEqual ( 3 ) ;
171
+ expect ( tracks . length ) . toEqual ( 3 ) ;
172
+ expect ( tracks [ 0 ] . id ) . toEqual ( 2 ) ;
173
+ expect ( tracks [ 0 ] . lastTimestamp ) . toEqual ( 900 ) ;
174
+ expect ( tracks [ 1 ] . id ) . toEqual ( 3 ) ;
175
+ expect ( tracks [ 1 ] . lastTimestamp ) . toEqual ( 900 ) ;
176
+ expect ( tracks [ 2 ] . id ) . toEqual ( 1 ) ;
177
+ expect ( tracks [ 2 ] . lastTimestamp ) . toEqual ( 100 ) ;
178
+
179
+ // Timestamp: 1200. First pose spawns a new track (id = 4), even though it
180
+ // has the same keypoints as track 1. This is because the age exceeds 1000
181
+ // msec. The second pose links with id 2. The third pose spawns a new
182
+ // track (id = 5).
183
+ poses = [
184
+ { keypoints : [ // Becomes id = 4.
185
+ { x : 0.2 , y : 0.2 , score : 1.0 } ,
186
+ { x : 0.4 , y : 0.4 , score : 0.8 } ,
187
+ { x : 0.6 , y : 0.6 , score : 0.9 } ,
188
+ { x : 0.8 , y : 0.8 , score : 0.8 }
189
+ ] } ,
190
+ { keypoints : [ // Links with id = 2.
191
+ { x : 0.55 , y : 0.7 , score : 0.7 } ,
192
+ { x : 0.5 , y : 0.6 , score : 0.9 } ,
193
+ { x : 1.0 , y : 1.0 , score : 0.1 } , // Low confidence.
194
+ { x : 0.8 , y : 0.1 , score : 0.0 } // Low confidence.
195
+ ] } ,
196
+ { keypoints : [ // Becomes id = 5.
197
+ { x : 0.1 , y : 0.1 , score : 0.1 } , // Low confidence.
198
+ { x : 0.2 , y : 0.2 , score : 0.9 } ,
199
+ { x : 0.3 , y : 0.3 , score : 0.7 } ,
200
+ { x : 0.4 , y : 0.4 , score : 0.8 }
201
+ ] } ,
202
+ ] ;
203
+ poses = kptTracker . apply ( poses , 1200 ) ;
204
+ tracks = kptTracker . getTracks ( ) ;
205
+ expect ( poses . length ) . toEqual ( 3 ) ;
206
+ expect ( poses [ 0 ] . id ) . toEqual ( 4 ) ;
207
+ expect ( poses [ 1 ] . id ) . toEqual ( 2 ) ;
208
+ expect ( tracks . length ) . toEqual ( 4 ) ;
209
+ expect ( tracks [ 0 ] . id ) . toEqual ( 2 ) ;
210
+ expect ( tracks [ 0 ] . lastTimestamp ) . toEqual ( 1200 ) ;
211
+ expect ( tracks [ 1 ] . id ) . toEqual ( 4 ) ;
212
+ expect ( tracks [ 1 ] . lastTimestamp ) . toEqual ( 1200 ) ;
213
+ expect ( tracks [ 2 ] . id ) . toEqual ( 5 ) ;
214
+ expect ( tracks [ 2 ] . lastTimestamp ) . toEqual ( 1200 ) ;
215
+ expect ( tracks [ 3 ] . id ) . toEqual ( 3 ) ;
216
+ expect ( tracks [ 3 ] . lastTimestamp ) . toEqual ( 900 ) ;
217
+
218
+ // Timestamp: 1300. First pose spawns a new track (id = 6). Since maxTracks
219
+ // is 4, the oldest track (id = 3) is removed.
220
+ poses = [
221
+ { keypoints : [ // Becomes id = 6.
222
+ { x : 0.1 , y : 0.8 , score : 1.0 } ,
223
+ { x : 0.2 , y : 0.9 , score : 0.6 } ,
224
+ { x : 0.2 , y : 0.9 , score : 0.5 } ,
225
+ { x : 0.8 , y : 0.2 , score : 0.4 }
226
+ ] } ,
227
+ ] ;
228
+ poses = kptTracker . apply ( poses , 1300 ) ;
229
+ tracks = kptTracker . getTracks ( ) ;
230
+ expect ( poses . length ) . toEqual ( 1 ) ;
231
+ expect ( poses [ 0 ] . id ) . toEqual ( 6 ) ;
232
+ expect ( tracks . length ) . toEqual ( 4 ) ;
233
+ expect ( tracks [ 0 ] . id ) . toEqual ( 6 ) ;
234
+ expect ( tracks [ 0 ] . lastTimestamp ) . toEqual ( 1300 ) ;
235
+ expect ( tracks [ 1 ] . id ) . toEqual ( 2 ) ;
236
+ expect ( tracks [ 1 ] . lastTimestamp ) . toEqual ( 1200 ) ;
237
+ expect ( tracks [ 2 ] . id ) . toEqual ( 4 ) ;
238
+ expect ( tracks [ 2 ] . lastTimestamp ) . toEqual ( 1200 ) ;
239
+ expect ( tracks [ 3 ] . id ) . toEqual ( 5 ) ;
240
+ expect ( tracks [ 3 ] . lastTimestamp ) . toEqual ( 1200 ) ;
241
+ } ) ;
242
+ } ) ;
0 commit comments