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