Skip to content

Commit 3385dce

Browse files
author
Gareth
authored
Merge pull request #375 from Labelbox/gj/video-mal
2 parents f15f3ab + c836ff7 commit 3385dce

File tree

2 files changed

+343
-10
lines changed

2 files changed

+343
-10
lines changed

examples/model_assisted_labeling/image_mal.ipynb

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

0 commit comments

Comments
 (0)