1
+ {
2
+ "cells" : [
3
+ {
4
+ "cell_type" : " markdown" ,
5
+ "id" : " stupid-court" ,
6
+ "metadata" : {},
7
+ "source" : [
8
+ " # Video MAL"
9
+ ]
10
+ },
11
+ {
12
+ "cell_type" : " markdown" ,
13
+ "id" : " intellectual-idaho" ,
14
+ "metadata" : {},
15
+ "source" : [
16
+ " * Upload model inferences for video tasks\n " ,
17
+ " * Support types\n " ,
18
+ " * bounding box"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type" : " code" ,
23
+ "execution_count" : 1 ,
24
+ "id" : " voluntary-minister" ,
25
+ "metadata" : {},
26
+ "outputs" : [],
27
+ "source" : [
28
+ " %%capture\n " ,
29
+ " !pip install labelbox"
30
+ ]
31
+ },
32
+ {
33
+ "cell_type" : " code" ,
34
+ "execution_count" : 2 ,
35
+ "id" : " committed-richards" ,
36
+ "metadata" : {},
37
+ "outputs" : [],
38
+ "source" : [
39
+ " import os\n " ,
40
+ " import uuid\n " ,
41
+ " from io import BytesIO\n " ,
42
+ " from typing import Dict, Any, Tuple\n " ,
43
+ " \n " ,
44
+ " from labelbox import Client, LabelingFrontend\n " ,
45
+ " from labelbox.schema.ontology import OntologyBuilder, Tool, Classification, Option"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type" : " code" ,
50
+ "execution_count" : 4 ,
51
+ "id" : " thirty-grocery" ,
52
+ "metadata" : {},
53
+ "outputs" : [],
54
+ "source" : [
55
+ " API_KEY = os.environ.get(\" LABELBOX_API_KEY\" )\n " ,
56
+ " if not API_KEY:\n " ,
57
+ " raise EnvironmentError(\" Missing API Key\" )"
58
+ ]
59
+ },
60
+ {
61
+ "cell_type" : " code" ,
62
+ "execution_count" : 5 ,
63
+ "id" : " conservative-marsh" ,
64
+ "metadata" : {},
65
+ "outputs" : [],
66
+ "source" : [
67
+ " # Only update this if you have an on-prem deployment\n " ,
68
+ " ENDPOINT = \" https://api.labelbox.com/graphql\" "
69
+ ]
70
+ },
71
+ {
72
+ "cell_type" : " code" ,
73
+ "execution_count" : 6 ,
74
+ "id" : " affecting-myanmar" ,
75
+ "metadata" : {},
76
+ "outputs" : [],
77
+ "source" : [
78
+ " client = Client(\n " ,
79
+ " api_key=API_KEY,\n " ,
80
+ " endpoint=ENDPOINT\n " ,
81
+ " )"
82
+ ]
83
+ },
84
+ {
85
+ "cell_type" : " markdown" ,
86
+ "id" : " blessed-venture" ,
87
+ "metadata" : {},
88
+ "source" : [
89
+ " ### Project Setup"
90
+ ]
91
+ },
92
+ {
93
+ "cell_type" : " code" ,
94
+ "execution_count" : 7 ,
95
+ "id" : " suburban-crowd" ,
96
+ "metadata" : {},
97
+ "outputs" : [],
98
+ "source" : [
99
+ " # We want to try out a few different tools here.\n " ,
100
+ " ontology_builder = OntologyBuilder(\n " ,
101
+ " tools=[\n " ,
102
+ " Tool(tool=Tool.Type.BBOX, name=\" jellyfish\" )\n " ,
103
+ " ]\n " ,
104
+ " )"
105
+ ]
106
+ },
107
+ {
108
+ "cell_type" : " code" ,
109
+ "execution_count" : 8 ,
110
+ "id" : " modern-program" ,
111
+ "metadata" : {},
112
+ "outputs" : [],
113
+ "source" : [
114
+ " # Lets setup a project to label\n " ,
115
+ " # Note see Ontology, Project, and Project_setup notebooks for more information on this section.\n " ,
116
+ " project = client.create_project(name=\" video_mal_project\" )\n " ,
117
+ " dataset = client.create_dataset(name=\" video_mal_dataset\" )\n " ,
118
+ " dataset.create_data_row(\n " ,
119
+ " row_data=\" https://storage.labelbox.com/cjhfn5y6s0pk507024nz1ocys%2Fb8837f3b-b071-98d9-645e-2e2c0302393b-jellyfish2-100-110.mp4\" )\n " ,
120
+ " editor = next(\n " ,
121
+ " client.get_labeling_frontends(where=LabelingFrontend.name == \" Editor\" )\n " ,
122
+ " )\n " ,
123
+ " project.setup(editor, ontology_builder.asdict())\n " ,
124
+ " project.datasets.connect(dataset)"
125
+ ]
126
+ },
127
+ {
128
+ "cell_type" : " code" ,
129
+ "execution_count" : 9 ,
130
+ "id" : " vietnamese-kingdom" ,
131
+ "metadata" : {},
132
+ "outputs" : [
133
+ {
134
+ "data" : {
135
+ "text/plain" : [
136
+ " True"
137
+ ]
138
+ },
139
+ "execution_count" : 9 ,
140
+ "metadata" : {},
141
+ "output_type" : " execute_result"
142
+ }
143
+ ],
144
+ "source" : [
145
+ " project.enable_model_assisted_labeling()"
146
+ ]
147
+ },
148
+ {
149
+ "cell_type" : " markdown" ,
150
+ "id" : " portable-grenada" ,
151
+ "metadata" : {},
152
+ "source" : [
153
+ " #### Grab featureSchemaIds"
154
+ ]
155
+ },
156
+ {
157
+ "cell_type" : " code" ,
158
+ "execution_count" : 10 ,
159
+ "id" : " abstract-fifteen" ,
160
+ "metadata" : {},
161
+ "outputs" : [
162
+ {
163
+ "name" : " stdout" ,
164
+ "output_type" : " stream" ,
165
+ "text" : [
166
+ " {'jellyfish': 'cky3dt2lja37d0z9t26wf3qo5'}\n "
167
+ ]
168
+ }
169
+ ],
170
+ "source" : [
171
+ " # When we created a project with the ontology defined above, all of the ids were assigned.\n " ,
172
+ " # So lets reconstruct the ontology builder with all of the ids.\n " ,
173
+ " ontology = ontology_builder.from_project(project)\n " ,
174
+ " # We want all of the feature schemas to be easily accessible by name.\n " ,
175
+ " schema_lookup = {tool.name: tool.feature_schema_id for tool in ontology.tools}\n " ,
176
+ " print(schema_lookup)"
177
+ ]
178
+ },
179
+ {
180
+ "cell_type" : " markdown" ,
181
+ "id" : " portuguese-arthur" ,
182
+ "metadata" : {},
183
+ "source" : [
184
+ " ## Import Format\n " ,
185
+ " \n " ,
186
+ " * [Documentation](https://docs.labelbox.com/docs/bounding-box-json)\n " ,
187
+ " \n " ,
188
+ " \n " ,
189
+ " ```\n " ,
190
+ " Each row of the import is a unique instance\n " ,
191
+ " \n " ,
192
+ " schemaId: <featureSchemaId>\n " ,
193
+ " dataRow:\n " ,
194
+ " id: <dataRowId>\n " ,
195
+ " Instance:\n " ,
196
+ " [Segments]:\n " ,
197
+ " [KeyFrames]:\n " ,
198
+ " frame:\n " ,
199
+ " bbox:\n " ,
200
+ " top:\n " ,
201
+ " bottom:\n " ,
202
+ " height:\n " ,
203
+ " width:\n " ,
204
+ " ```\n " ,
205
+ " \n " ,
206
+ " **segments**: A segment represents a continuous section where an object is visible. If an instance disappears then the segment ends. If it re-appears, a new segment is created.\n " ,
207
+ " \n " ,
208
+ " **keyframes**: Key frames identify the location of an instance. Between keyframes, the location of the instance is interpolated.\n " ,
209
+ " \n " ,
210
+ " **bbox**: The coordinates of the bounding box"
211
+ ]
212
+ },
213
+ {
214
+ "cell_type" : " code" ,
215
+ "execution_count" : 11 ,
216
+ "id" : " 5fc417c5" ,
217
+ "metadata" : {},
218
+ "outputs" : [],
219
+ "source" : [
220
+ " segments = [\n " ,
221
+ " {\n " ,
222
+ " \" keyframes\" : [\n " ,
223
+ " {\n " ,
224
+ " \" frame\" : 1,\n " ,
225
+ " \" bbox\" : {\n " ,
226
+ " \" top\" : 80,\n " ,
227
+ " \" left\" : 80,\n " ,
228
+ " \" height\" : 80,\n " ,
229
+ " \" width\" : 80\n " ,
230
+ " }\n " ,
231
+ " },\n " ,
232
+ " {\n " ,
233
+ " \" frame\" : 20,\n " ,
234
+ " \" bbox\" : {\n " ,
235
+ " \" top\" : 125,\n " ,
236
+ " \" left\" : 125,\n " ,
237
+ " \" height\" : 200,\n " ,
238
+ " \" width\" : 300\n " ,
239
+ " }\n " ,
240
+ " }\n " ,
241
+ " ]\n " ,
242
+ " },\n " ,
243
+ " {\n " ,
244
+ " \" keyframes\" : [\n " ,
245
+ " {\n " ,
246
+ " \" frame\" : 27,\n " ,
247
+ " \" bbox\" : {\n " ,
248
+ " \" top\" : 80,\n " ,
249
+ " \" left\" : 50,\n " ,
250
+ " \" height\" : 80,\n " ,
251
+ " \" width\" : 50\n " ,
252
+ " }\n " ,
253
+ " }\n " ,
254
+ " ]\n " ,
255
+ " }\n " ,
256
+ " ]"
257
+ ]
258
+ },
259
+ {
260
+ "cell_type" : " markdown" ,
261
+ "id" : " convertible-entry" ,
262
+ "metadata" : {},
263
+ "source" : [
264
+ " ##### Create helper functions to make this much easier"
265
+ ]
266
+ },
267
+ {
268
+ "cell_type" : " code" ,
269
+ "execution_count" : 12 ,
270
+ "id" : " developing-beauty" ,
271
+ "metadata" : {},
272
+ "outputs" : [],
273
+ "source" : [
274
+ " def create_video_bbox_ndjson(datarow_id: str, schema_id: str, segments: Dict[str, Any]) -> Dict[str, Any]:\n " ,
275
+ " return {\n " ,
276
+ " \" uuid\" : str(uuid.uuid4()),\n " ,
277
+ " \" schemaId\" : schema_id,\n " ,
278
+ " \" dataRow\" : {\" id\" : datarow_id},\n " ,
279
+ " \" segments\" : segments\n " ,
280
+ " }"
281
+ ]
282
+ },
283
+ {
284
+ "cell_type" : " code" ,
285
+ "execution_count" : 13 ,
286
+ "id" : " asian-savings" ,
287
+ "metadata" : {},
288
+ "outputs" : [],
289
+ "source" : [
290
+ " uploads = []\n " ,
291
+ " \n " ,
292
+ " for data_row in dataset.data_rows():\n " ,
293
+ " uploads.append(create_video_bbox_ndjson(data_row.uid, schema_lookup['jellyfish'], segments))"
294
+ ]
295
+ },
296
+ {
297
+ "cell_type" : " markdown" ,
298
+ "id" : " perfect-seafood" ,
299
+ "metadata" : {},
300
+ "source" : [
301
+ " ### Upload the annotations"
302
+ ]
303
+ },
304
+ {
305
+ "cell_type" : " code" ,
306
+ "execution_count" : 14 ,
307
+ "id" : " entire-community" ,
308
+ "metadata" : {},
309
+ "outputs" : [],
310
+ "source" : [
311
+ " # Let's upload!\n " ,
312
+ " # Validate must be set to false for video bounding boxes\n " ,
313
+ " upload_task = project.upload_annotations(name=f\" upload-job-{uuid.uuid4()}\" ,\n " ,
314
+ " annotations=uploads,\n " ,
315
+ " validate=False)"
316
+ ]
317
+ },
318
+ {
319
+ "cell_type" : " code" ,
320
+ "execution_count" : 15 ,
321
+ "id" : " hollywood-faculty" ,
322
+ "metadata" : {},
323
+ "outputs" : [
324
+ {
325
+ "name" : " stdout" ,
326
+ "output_type" : " stream" ,
327
+ "text" : [
328
+ " []\n "
329
+ ]
330
+ }
331
+ ],
332
+ "source" : [
333
+ " # Wait for upload to finish (Will take up to five minutes)\n " ,
334
+ " upload_task.wait_until_done()\n " ,
335
+ " # Review the upload status\n " ,
336
+ " print(upload_task.errors)"
337
+ ]
338
+ }
339
+ ],
340
+ "metadata" : {
341
+ "kernelspec" : {
342
+ "display_name" : " Python 3 (ipykernel)" ,
343
+ "language" : " python" ,
344
+ "name" : " python3"
345
+ },
346
+ "language_info" : {
347
+ "codemirror_mode" : {
348
+ "name" : " ipython" ,
349
+ "version" : 3
350
+ },
351
+ "file_extension" : " .py" ,
352
+ "mimetype" : " text/x-python" ,
353
+ "name" : " python" ,
354
+ "nbconvert_exporter" : " python" ,
355
+ "pygments_lexer" : " ipython3" ,
356
+ "version" : " 3.8.2"
357
+ }
358
+ },
359
+ "nbformat" : 4 ,
360
+ "nbformat_minor" : 5
361
+ }
0 commit comments