diff --git a/examples/README.md b/examples/README.md index eda753467..b10d7dde5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,20 +18,19 @@ | Ontologies | [Github](basics/ontologies.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/basics/ontologies.ipynb) | | Projects | [Github](basics/projects.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/basics/projects.ipynb) | | User Management | [Github](basics/user_management.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/basics/user_management.ipynb) | +| Export V1 to V2 migration | [Github](basics/Export_V1_to_V2_migration_support.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/basics/Export_V1_to_V2_migration_support.ipynb) | --- +## [Exports](exports) -## [Model Training](https://docs.labelbox.com/docs/integration-with-model-training-service) - -Train a model using data annotated on Labelbox - -| Notebook | Github | Google Colab | -| ------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Object Detection (Detectron2) | [Github](integrations/detectron2/coco_object.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/integrations/detectron2/coco_object.ipynb) | -| Panoptic Detection (Detectron2) | [Github](integrations/detectron2/coco_panoptic.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/integrations/detectron2/coco_panoptic.ipynb) | +| Notebook | Github | Google Colab | Learn more | +| ------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| Export data | [Github](exports/export_data.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/exports/export_data.ipynb) | +| Composite mask export | [Github](exports/composite_mask_export.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Labelbox/labelbox-python/blob/master/examples/exports/composite_mask_export.ipynb) | --- + ## [Annotation Import (Ground Truth & MAL)](annotation_import) | Notebook | Github | Google Colab | Learn more | diff --git a/examples/annotation_import/pdf.ipynb b/examples/annotation_import/pdf.ipynb index f0a012e5c..349feabb8 100644 --- a/examples/annotation_import/pdf.ipynb +++ b/examples/annotation_import/pdf.ipynb @@ -79,6 +79,8 @@ "metadata": {}, "source": [ "import uuid\n", + "import json\n", + "import requests\n", "import labelbox as lb\n", "import labelbox.types as lb_types\n" ], @@ -133,7 +135,7 @@ ")\n", "\n", "# NDJSON\n", - "entities_annotations_ndjson = { \n", + "entities_annotations_ndjson = {\n", " \"name\": \"named_entity\",\n", " \"textSelections\": [\n", " {\n", @@ -155,10 +157,10 @@ "source": [ "########### Radio Classification #########\n", "\n", - "# Annotation types \n", + "# Annotation types\n", "radio_annotation = lb_types.ClassificationAnnotation(\n", " name=\"radio_question\",\n", - " value=lb_types.Radio(answer = \n", + " value=lb_types.Radio(answer =\n", " lb_types.ClassificationAnswer(name = \"first_radio_answer\")\n", " )\n", ")\n", @@ -177,7 +179,7 @@ "source": [ "############ Checklist Classification ###########\n", "\n", - "# Annotation types \n", + "# Annotation types\n", "checklist_annotation = lb_types.ClassificationAnnotation(\n", " name=\"checklist_question\",\n", " value=lb_types.Checklist(answer = [\n", @@ -208,7 +210,7 @@ "bbox_annotation = lb_types.ObjectAnnotation(\n", " name=\"bounding_box\", # must match your ontology feature\"s name\n", " value=lb_types.DocumentRectangle(\n", - " start=lb_types.Point(x=102.771, y=135.3), # x = left, y = top \n", + " start=lb_types.Point(x=102.771, y=135.3), # x = left, y = top\n", " end=lb_types.Point(x=518.571, y=245.143), # x= left + width , y = top + height\n", " page=0,\n", " unit=lb_types.RectangleUnit.POINTS\n", @@ -257,13 +259,13 @@ "nested_checklist_annotation_ndjson = {\n", " \"name\": \"nested_checklist_question\",\n", " \"answer\": [{\n", - " \"name\": \"first_checklist_answer\", \n", + " \"name\": \"first_checklist_answer\",\n", " \"classifications\" : [\n", " {\n", - " \"name\": \"sub_checklist_question\", \n", + " \"name\": \"sub_checklist_question\",\n", " \"answer\": {\"name\": \"first_sub_checklist_answer\"}\n", - " } \n", - " ] \n", + " }\n", + " ]\n", " }]\n", "}\n", "\n", @@ -306,7 +308,7 @@ { "metadata": {}, "source": [ - "############## Classification Free-form text ############## \n", + "############## Classification Free-form text ##############\n", "\n", "text_annotation = lb_types.ClassificationAnnotation(\n", " name=\"free_text\", # must match your ontology feature\"s name\n", @@ -331,7 +333,7 @@ "bbox_with_radio_subclass_annotation = lb_types.ObjectAnnotation(\n", " name=\"bbox_with_radio_subclass\",\n", " value=lb_types.DocumentRectangle(\n", - " start=lb_types.Point(x=317.271, y=226.757), # x = left, y = top \n", + " start=lb_types.Point(x=317.271, y=226.757), # x = left, y = top\n", " end=lb_types.Point(x=566.657, y=420.986), # x= left + width , y = top + height\n", " unit=lb_types.RectangleUnit.POINTS,\n", " page=1\n", @@ -362,12 +364,12 @@ " \"name\": \"bbox_with_radio_subclass\",\n", " \"classifications\": [\n", " {\n", - " \"name\": \"sub_radio_question\", \n", + " \"name\": \"sub_radio_question\",\n", " \"answer\": {\n", - " \"name\": \"first_sub_radio_answer\", \n", + " \"name\": \"first_sub_radio_answer\",\n", " \"classifications\": [\n", " {\n", - " \"name\": \"second_sub_radio_question\", \n", + " \"name\": \"second_sub_radio_question\",\n", " \"answer\": {\n", " \"name\": \"second_sub_radio_answer\"}\n", " }\n", @@ -392,7 +394,7 @@ { "metadata": {}, "source": [ - "############ NER with nested classifications ######## \n", + "############ NER with nested classifications ########\n", "\n", "\n", "ner_with_checklist_subclass_annotation = lb_types.ObjectAnnotation(\n", @@ -423,7 +425,7 @@ " \"classifications\":[\n", " {\n", " \"name\": \"sub_checklist_question\",\n", - " \"answer\": [{\"name\": \"first_sub_checklist_answer\"}] \n", + " \"answer\": [{\"name\": \"first_sub_checklist_answer\"}]\n", " }\n", " ],\n", " \"textSelections\": [\n", @@ -434,9 +436,9 @@ " \"groupId\": \"\",\n", " \"page\": 1\n", " }\n", - " ] \n", + " ]\n", "}\n", - " \n" + "\n" ], "cell_type": "code", "outputs": [], @@ -445,7 +447,7 @@ { "metadata": {}, "source": [ - "######### Relationships ########## \n", + "######### Relationships ##########\n", "entity_source = lb_types.ObjectAnnotation(\n", " name=\"named_entity\",\n", " value= lb_types.DocumentEntity(\n", @@ -482,7 +484,7 @@ " type=lb_types.Relationship.Type.UNIDIRECTIONAL,\n", " ))\n", "\n", - "## Only supported for MAL imports \n", + "## Only supported for MAL imports\n", "uuid_source = str(uuid.uuid4())\n", "uuid_target = str(uuid.uuid4())\n", "\n", @@ -498,7 +500,7 @@ " \"page\": 1\n", " }\n", " ]\n", - " \n", + "\n", "}\n", "\n", "entity_target_ndjson = {\n", @@ -515,7 +517,7 @@ " ]\n", "}\n", "ner_relationship_annotation_ndjson = {\n", - " \"name\": \"relationship\", \n", + " \"name\": \"relationship\",\n", " \"relationship\": {\n", " \"source\": uuid_source,\n", " \"target\": uuid_target,\n", @@ -537,7 +539,7 @@ "bbox_source = lb_types.ObjectAnnotation(\n", " name=\"bounding_box\",\n", " value=lb_types.DocumentRectangle(\n", - " start=lb_types.Point(x=188.257, y=68.875), # x = left, y = top \n", + " start=lb_types.Point(x=188.257, y=68.875), # x = left, y = top\n", " end=lb_types.Point(x=270.907, y=149.556), # x = left + width , y = top + height\n", " unit=lb_types.RectangleUnit.POINTS,\n", " page=1\n", @@ -563,7 +565,7 @@ " ))\n", "\n", "\n", - "## Only supported for MAL imports \n", + "## Only supported for MAL imports\n", "uuid_source_2 = str(uuid.uuid4())\n", "uuid_target_2 = str(uuid.uuid4())\n", "\n", @@ -594,7 +596,7 @@ "}\n", "\n", "bbox_relationship_annotation_ndjson = {\n", - " \"name\": \"relationship\", \n", + " \"name\": \"relationship\",\n", " \"relationship\": {\n", " \"source\": uuid_source_2,\n", " \"target\": uuid_target_2,\n", @@ -623,12 +625,27 @@ { "metadata": {}, "source": [ - "## Text layer url is required for uploading entity annotations\n", - "global_key = \"0801.3483.pdf\"\n", + "Passing a `text_layer_url` is not longer required. Labelbox automatically generates a text layer using Google Document AI and its OCR engine to detect tokens. \n", + "\n", + "However, it's important to note that Google Document AI imposes specific restrictions on document size:\n", + "- The document must have no more than 15 pages.\n", + "- The file size should not exceed 20 MB.\n", + "\n", + "Furthermore, Google Document AI optimizes documents before OCR processing. This optimization might include rotating images or pages to ensure that text appears horizontally. Consequently, token coordinates are calculated based on the rotated/optimized images, resulting in potential discrepancies with the original PDF document.\n", + "\n", + "For example, in a landscape-oriented PDF, the document is rotated by 90 degrees before processing. As a result, all tokens in the text layer are also rotated by 90 degrees.\n", + "\n", + "You may still pass a `text_layer_url` if you wish to bypass the automatic text layer generation\n" + ], + "cell_type": "markdown" + }, + { + "metadata": {}, + "source": [ + "global_key = \"0801.3483_doc.pdf\"\n", "img_url = {\n", " \"row_data\": {\n", - " \"pdf_url\": \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483.pdf\",\n", - " \"text_layer_url\": \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483-lb-textlayer.json\"\n", + " \"pdf_url\": \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483.pdf\"\n", " },\n", " \"global_key\": global_key\n", "}\n", @@ -637,8 +654,15 @@ "dataset = client.create_dataset(name=\"pdf_demo_dataset\")\n", "task = dataset.create_data_rows([img_url])\n", "task.wait_till_done()\n", - "print(\"Errors:\",task.errors)\n", - "print(\"Failed data rows:\", task.failed_data_rows)" + "print(f\"Failed data rows: {task.failed_data_rows}\")\n", + "print(f\"Errors: {task.errors}\")\n", + "\n", + "if task.errors:\n", + " for error in task.errors:\n", + " if 'Duplicate global key' in error['message'] and dataset.row_count == 0:\n", + " # If the global key already exists in the workspace the dataset will be created empty, so we can delete it.\n", + " print(f\"Deleting empty dataset: {dataset}\")\n", + " dataset.delete()" ], "cell_type": "code", "outputs": [], @@ -659,9 +683,9 @@ "\n", "ontology_builder = lb.OntologyBuilder(\n", " classifications=[ # List of Classification objects\n", - " lb.Classification( \n", + " lb.Classification(\n", " class_type=lb.Classification.Type.RADIO,\n", - " name=\"radio_question\", \n", + " name=\"radio_question\",\n", " scope = lb.Classification.Scope.GLOBAL,\n", " options=[\n", " lb.Option(value=\"first_radio_answer\"),\n", @@ -670,13 +694,13 @@ " ),\n", " lb.Classification(\n", " class_type=lb.Classification.Type.CHECKLIST,\n", - " name=\"checklist_question\", \n", + " name=\"checklist_question\",\n", " scope = lb.Classification.Scope.GLOBAL,\n", " options=[\n", " lb.Option(value=\"first_checklist_answer\"),\n", " lb.Option(value=\"second_checklist_answer\")\n", " ]\n", - " ), \n", + " ),\n", " lb.Classification(\n", " class_type=lb.Classification.Type.TEXT,\n", " name=\"free_text\",\n", @@ -706,15 +730,15 @@ " options=[\n", " lb.Classification(\n", " class_type=lb.Classification.Type.CHECKLIST,\n", - " name=\"sub_checklist_question\", \n", + " name=\"sub_checklist_question\",\n", " options=[lb.Option(\"first_sub_checklist_answer\")]\n", " )\n", " ])\n", " ]\n", - " ), \n", + " ),\n", " ],\n", " tools=[ # List of Tool objects\n", - " lb.Tool( tool=lb.Tool.Type.BBOX,name=\"bounding_box\"), \n", + " lb.Tool( tool=lb.Tool.Type.BBOX,name=\"bounding_box\"),\n", " lb.Tool(tool=lb.Tool.Type.NER, name=\"named_entity\"),\n", " lb.Tool(tool=lb.Tool.Type.RELATIONSHIP,name=\"relationship\"),\n", " lb.Tool(tool=lb.Tool.Type.NER,\n", @@ -767,7 +791,7 @@ "metadata": {}, "source": [ "# Create a Labelbox project\n", - "project = client.create_project(name=\"PDF_annotation_demo\", \n", + "project = client.create_project(name=\"PDF_annotation_demo\",\n", " media_type=lb.MediaType.Document)\n", "project.setup_editor(ontology)" ], @@ -811,17 +835,39 @@ "metadata": {}, "source": [ "##### Step 5.1: First, we need to populate the text selections for Entity annotations\n", - "To learn how to generate a text layer for your documents please refer to the following repositories/files: \n", - "https://github.com/Labelbox/PDF-OCR-Transform-CLI/blob/main/src/scripts/gcloud/gcp-vision-to-lb-text-layer.py\n", - "https://github.com/Labelbox/PDF-OCR-Transform-CLI/blob/main/src/scripts/adobe/adobe-ocr-to-lb-text-layer.py\n" + "To import ner annotations, you must pass a `text_layer_url`, Labelbox automatically generates a `text_layer_url` after importing a pdf asset that doesn't include a `text_layer_url`\n" ], "cell_type": "markdown" }, { "metadata": {}, "source": [ - "import requests\n", - "import json\n", + "To extract the generated text layer url we first need to export the data row" + ], + "cell_type": "markdown" + }, + { + "metadata": {}, + "source": [ + "\n", + "client.enable_experimental = True\n", + "task = lb.DataRow.export(client=client,global_keys=[global_key])\n", + "task.wait_till_done()\n", + "stream = task.get_stream()\n", + "\n", + "text_layer = \"\"\n", + "for output in stream:\n", + " output_json = json.loads(output.json_str)\n", + " text_layer = output_json['media_attributes']['text_layer_url']\n", + "print(text_layer)" + ], + "cell_type": "code", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "source": [ "\n", "# Helper method\n", "def update_text_selections(annotation, group_id, list_tokens, page):\n", @@ -834,16 +880,14 @@ " }\n", " ]\n", " })\n", - " \n", "\n", - "text_layer = \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483-lb-textlayer.json\"\n", "\n", "# Fetch the content of the text layer\n", - "res = requests.get(text_layer) \n", + "res = requests.get(text_layer)\n", "\n", "# Phrases that we want to annotation obtained from the text layer url\n", - "content_phrases = [\"Metal-insulator (MI) transitions have been one of the\" , \n", - " \"T. Sasaki,* N. Yoneyama, and N. Kobayashi\", \n", + "content_phrases = [\"Metal-insulator (MI) transitions have been one of the\" ,\n", + " \"T. Sasaki, N. Yoneyama, and N. Kobayashi\",\n", " \"Organic charge transfer salts based on the donor\",\n", " \"the experimental investigations on this issue have not\"]\n", "\n", @@ -862,7 +906,7 @@ " text_selections.append(document_text_selection)\n", " # build text selection for the NDJson annotations\n", " update_text_selections(annotation=entities_annotations_ndjson,\n", - " group_id=group[\"id\"], # id representing group of words \n", + " group_id=group[\"id\"], # id representing group of words\n", " list_tokens=list_tokens, # ids representing individual words from the group\n", " page=1)\n", " if group[\"content\"] == content_phrases[1]:\n", @@ -872,7 +916,7 @@ " text_selections_ner.append(ner_text_selection)\n", " # build text selection for the NDJson annotations\n", " update_text_selections(annotation=ner_with_checklist_subclass_annotation_ndjson,\n", - " group_id=group[\"id\"], # id representing group of words \n", + " group_id=group[\"id\"], # id representing group of words\n", " list_tokens=list_tokens_2, # ids representing individual words from the group\n", " page=1)\n", " if group[\"content\"] == content_phrases[2]:\n", @@ -882,7 +926,7 @@ " text_selections_source.append(text_selection_entity_source)\n", " # build text selection for the NDJson annotations\n", " update_text_selections(annotation=entity_source_ndjson,\n", - " group_id=group[\"id\"], # id representing group of words \n", + " group_id=group[\"id\"], # id representing group of words\n", " list_tokens=relationship_source, # ids representing individual words from the group\n", " page=1)\n", " if group[\"content\"] == content_phrases[3]:\n", @@ -892,7 +936,7 @@ " text_selections_target.append(text_selection_entity_target)\n", " # build text selections forthe NDJson annotations\n", " update_text_selections(annotation=entity_target_ndjson,\n", - " group_id=group[\"id\"], # id representing group of words \n", + " group_id=group[\"id\"], # id representing group of words\n", " list_tokens=relationship_target, # ids representing individual words from the group\n", " page=1)\n" ], @@ -910,11 +954,11 @@ { "metadata": {}, "source": [ - "#re-write the entity annotation with text selections \n", + "#re-write the entity annotation with text selections\n", "entities_annotation_document_entity = lb_types.DocumentEntity(name=\"named_entity\", textSelections = text_selections)\n", "entities_annotation = lb_types.ObjectAnnotation(name=\"named_entity\",value=entities_annotation_document_entity)\n", "\n", - "# re-write the entity annotation + subclassification with text selections \n", + "# re-write the entity annotation + subclassification with text selections\n", "classifications = [\n", " lb_types.ClassificationAnnotation(\n", " name=\"sub_checklist_question\",\n", @@ -924,8 +968,8 @@ " )\n", " ]\n", "ner_annotation_with_subclass = lb_types.DocumentEntity(name=\"ner_with_checklist_subclass\", textSelections= text_selections_ner)\n", - "ner_with_checklist_subclass_annotation = lb_types.ObjectAnnotation(name=\"ner_with_checklist_subclass\", \n", - " value=ner_annotation_with_subclass, \n", + "ner_with_checklist_subclass_annotation = lb_types.ObjectAnnotation(name=\"ner_with_checklist_subclass\",\n", + " value=ner_annotation_with_subclass,\n", " classifications=classifications)\n", "\n", "#re-write the entity source and target annotations withe text selectios\n", @@ -935,15 +979,14 @@ "entity_target_doc = lb_types.DocumentEntity(name=\"named_entity\", text_selections=text_selections_target)\n", "entity_target = lb_types.ObjectAnnotation(name=\"named_entity\", value=entity_target_doc)\n", "\n", - "# re-write the entity relationship with the re-created entities \n", + "# re-write the entity relationship with the re-created entities\n", "entity_relationship = lb_types.RelationshipAnnotation(\n", " name=\"relationship\",\n", " value=lb_types.Relationship(\n", " source=entity_source,\n", " target=entity_target,\n", " type=lb_types.Relationship.Type.UNIDIRECTIONAL,\n", - " ))\n", - " " + " ))\n" ], "cell_type": "code", "outputs": [], @@ -952,7 +995,7 @@ { "metadata": {}, "source": [ - "# Final NDJSON and python annotations \n", + "# Final NDJSON and python annotations\n", "print(f\"entities_annotations_ndjson={entities_annotations_ndjson}\")\n", "print(f\"entities_annotation={entities_annotation}\")\n", "print(f\"nested_entities_annotation_ndjson={ner_with_checklist_subclass_annotation_ndjson}\")\n", @@ -986,19 +1029,19 @@ " annotations = [\n", " entities_annotation,\n", " checklist_annotation,\n", - " nested_checklist_annotation, \n", + " nested_checklist_annotation,\n", " text_annotation,\n", " radio_annotation,\n", " nested_radio_annotation,\n", " bbox_annotation,\n", " bbox_with_radio_subclass_annotation,\n", " ner_with_checklist_subclass_annotation,\n", - " entity_source, \n", - " entity_target, \n", - " entity_relationship,# Only supported for MAL imports \n", + " entity_source,\n", + " entity_target,\n", + " entity_relationship,# Only supported for MAL imports\n", " bbox_source,\n", " bbox_target,\n", - " bbox_relationship # Only supported for MAL imports \n", + " bbox_relationship # Only supported for MAL imports\n", " ]\n", " )\n", ")" @@ -1030,12 +1073,12 @@ " bbox_annotation_ndjson,\n", " bbox_with_radio_subclass_annotation_ndjson,\n", " ner_with_checklist_subclass_annotation_ndjson,\n", - " entity_source_ndjson, \n", - " entity_target_ndjson, \n", - " ner_relationship_annotation_ndjson, # Only supported for MAL imports \n", + " entity_source_ndjson,\n", + " entity_target_ndjson,\n", + " ner_relationship_annotation_ndjson, # Only supported for MAL imports\n", " bbox_source_ndjson,\n", " bbox_target_ndjson,\n", - " bbox_relationship_annotation_ndjson # Only supported for MAL imports \n", + " bbox_relationship_annotation_ndjson # Only supported for MAL imports\n", " ]:\n", " annot.update({\n", " \"dataRow\": {\"globalKey\": global_key},\n", @@ -1094,10 +1137,10 @@ "## Relationships are not currently supported for label import\n", "\n", "# upload_job = lb.LabelImport.create_from_objects(\n", - "# client = client, \n", - "# project_id = project.uid, \n", - "# name=\"label_import_job\"+str(uuid.uuid4()), \n", - "# labels=labels)\n", + "# client = client,\n", + "# project_id = project.uid,\n", + "# name=\"label_import_job\"+str(uuid.uuid4()),\n", + "# labels=labels) ## Remove unsupported relationships from the labels list\n", "\n", "# print(\"Errors:\", upload_job.errors)\n", "# print(\"Status of uploads: \", upload_job.statuses)" diff --git a/examples/basics/data_rows.ipynb b/examples/basics/data_rows.ipynb index 877cea78d..abe02fd86 100644 --- a/examples/basics/data_rows.ipynb +++ b/examples/basics/data_rows.ipynb @@ -47,7 +47,6 @@ " * HTML \n", " * DICOM \n", " * Conversational\n", - "* A data row is a member of a dataset \n", "* A data row cannot exist without belonging to a dataset.\n", "* Data rows are added to labeling tasks by first attaching them to datasets and then creating batches in projects" ], @@ -162,7 +161,6 @@ "\n", "if export_task.has_errors():\n", " export_task.get_stream(\n", - " \n", " converter=lb.JsonConverter(),\n", " stream_type=lb.StreamType.ERRORS\n", " ).start(stream_handler=lambda error: print(error))\n", @@ -249,7 +247,7 @@ " schema_id=mdo.reserved_by_name[\"tag\"].uid, # specify the schema id\n", " value=\"tag_string\", # typed inputs\n", " ),\n", - " ], \n", + " ],\n", " )" ], "cell_type": "code", @@ -330,6 +328,10 @@ " {\n", " \"type\": \"HTML\",\n", " \"value\": \"https://storage.googleapis.com/labelbox-sample-datasets/Docs/windy.html\"\n", + " },\n", + " {\n", + " \"type\": \"PDF_URL\",\n", + " \"value\": \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483.pdf\"\n", " }\n", " ]\n", " }])\n", @@ -413,7 +415,7 @@ { "metadata": {}, "source": [ - "# You can only create one attachment at the time. \n", + "# You can only create one attachment at the time.\n", "data_row.create_attachment(attachment_type=\"RAW_TEXT\",\n", " attachment_value=\"LABELERS WILL SEE THIS \")" ], diff --git a/examples/exports/composite_mask_export.ipynb b/examples/exports/composite_mask_export.ipynb index 5a352b3e5..dc46810fd 100644 --- a/examples/exports/composite_mask_export.ipynb +++ b/examples/exports/composite_mask_export.ipynb @@ -103,7 +103,7 @@ "```json \n", " {\n", " \"composite_mask\": {\n", - " \"url\": \"https://api.labelbox.com/api/v1/tasks/{task_id}/masks/{composite_mask_id}\",\n", + " \"url\": \"https://api.labelbox.com/api/v1/tasks/{task_id}/masks/{composite_mask_id}/index/1\",\n", " \"color_rgb\": [\n", " 142,\n", " 220,\n", @@ -113,7 +113,7 @@ " }\n", "```\n", " - ***Video example*** :\n", - " The export will adhere to the following URL convention by default. However, the image's URL convention is also considered valid.\n", + " The export will adhere to the following URL convention by default.\n", "```json\n", " {\n", " \"composite_mask\": {\n", @@ -138,7 +138,7 @@ "task_id = \"\"\n", "composite_mask_id = \"\"\n", "\n", - "mask_url = f'https://api.labelbox.com/api/v1/tasks/{task_id}/masks/{composite_mask_id}'\n", + "mask_url = f'https://api.labelbox.com/api/v1/tasks/{task_id}/masks/{composite_mask_id}/index/1'\n", "req = urllib.request.Request(mask_url, headers=client.headers)\n", "image = Image.open(urllib.request.urlopen(req))\n", "w, h = image.size\n", @@ -150,15 +150,15 @@ "cell_type": "code", "outputs": [ { + "output_type": "execute_result", "data": { "image/png": "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", "text/plain": [ "" ] }, - "execution_count": 15, "metadata": {}, - "output_type": "execute_result" + "execution_count": 15 } ], "execution_count": null @@ -177,7 +177,7 @@ " \"annotation_kind\": \"ImageSegmentationMask\",\n", " \"classifications\": [],\n", " \"composite_mask\": {\n", - " \"url\": \"https://api.labelbox.com/api/v1/tasks/{task_id}/masks/{composite_mask_id}\",\n", + " \"url\": \"https://api.labelbox.com/api/v1/tasks/{task_id}/masks/{composite_mask_id}/index/1\",\n", " \"color_rgb\": [\n", " 123,\n", " 103,\n", diff --git a/examples/prediction_upload/pdf_predictions.ipynb b/examples/prediction_upload/pdf_predictions.ipynb index 95aa0e39a..744bb10fa 100644 --- a/examples/prediction_upload/pdf_predictions.ipynb +++ b/examples/prediction_upload/pdf_predictions.ipynb @@ -43,6 +43,7 @@ "- Free text classifications\n", "- Bounding box\n", "- Entities\n", + "- Relationships (only supported for MAL imports)\n", "\n", "\n", "*NDJson*\n", @@ -50,7 +51,8 @@ "- Radio classifications (including nested classifications)\n", "- Free text classifications\n", "- Bounding box \n", - "- Entities " + "- Entities \n", + "- Relationships (only supported for MAL imports)" ], "cell_type": "markdown" }, @@ -73,9 +75,11 @@ { "metadata": {}, "source": [ + "import uuid\n", + "import json\n", + "import requests\n", "import labelbox as lb\n", - "import labelbox.types as lb_types\n", - "import uuid" + "import labelbox.types as lb_types" ], "cell_type": "code", "outputs": [], @@ -127,7 +131,7 @@ ")\n", "\n", "# NDJSON\n", - "entities_prediction_ndjson = { \n", + "entities_prediction_ndjson = {\n", " \"name\": \"named_entity\",\n", " \"confidence\": 0.5,\n", " \"textSelections\": [\n", @@ -150,10 +154,10 @@ "source": [ "########### Radio Classification #########\n", "\n", - "# Annotation types \n", + "# Annotation types\n", "radio_prediction = lb_types.ClassificationAnnotation(\n", " name=\"radio_question\",\n", - " value=lb_types.Radio(answer = \n", + " value=lb_types.Radio(answer =\n", " lb_types.ClassificationAnswer(name = \"first_radio_answer\", confidence=0.5)\n", " )\n", ")\n", @@ -172,7 +176,7 @@ "source": [ "############ Checklist Classification ###########\n", "\n", - "# Annotation types \n", + "# Annotation types\n", "checklist_prediction = lb_types.ClassificationAnnotation(\n", " name=\"checklist_question\",\n", " value=lb_types.Checklist(answer = [\n", @@ -209,7 +213,7 @@ "bbox_prediction = lb_types.ObjectAnnotation(\n", " name=\"bounding_box\", # must match your ontology feature\"s name\n", " value=lb_types.DocumentRectangle(\n", - " start=lb_types.Point(x=bbox_dim_1[\"left\"], y=bbox_dim_1[\"top\"]), # x = left, y = top \n", + " start=lb_types.Point(x=bbox_dim_1[\"left\"], y=bbox_dim_1[\"top\"]), # x = left, y = top\n", " end=lb_types.Point(x=bbox_dim_1[\"left\"] + bbox_dim_1[\"width\"], y=bbox_dim_1[\"top\"]+ bbox_dim_1[\"height\"]), # x= left + width , y = top + height\n", " page=0,\n", " unit=lb_types.RectangleUnit.POINTS\n", @@ -237,14 +241,14 @@ " value=lb_types.Checklist(\n", " answer=[lb_types.ClassificationAnswer(\n", " name=\"first_checklist_answer\",\n", - " confidence=0.5, # Confidence scores should be added to the answer \n", + " confidence=0.5, # Confidence scores should be added to the answer\n", " classifications=[\n", " lb_types.ClassificationAnnotation(\n", " name=\"sub_checklist_question\",\n", " value=lb_types.Checklist(\n", " answer=[lb_types.ClassificationAnswer(\n", " name=\"first_sub_checklist_answer\",\n", - " confidence=0.5 # Confidence scores should be added to the answer \n", + " confidence=0.5 # Confidence scores should be added to the answer\n", " )]\n", " ))\n", " ]\n", @@ -255,17 +259,17 @@ "nested_checklist_prediction_ndjson = {\n", " \"name\": \"nested_checklist_question\",\n", " \"answer\": [{\n", - " \"name\": \"first_checklist_answer\", \n", - " \"confidence\": 0.5, # Confidence scores should be added to the answer \n", + " \"name\": \"first_checklist_answer\",\n", + " \"confidence\": 0.5, # Confidence scores should be added to the answer\n", " \"classifications\" : [\n", " {\n", - " \"name\": \"sub_checklist_question\", \n", + " \"name\": \"sub_checklist_question\",\n", " \"answer\": {\n", - " \"name\": \"first_sub_checklist_answer\", \n", - " \"confidence\": 0.5, # Confidence scores should be added to the answer \n", + " \"name\": \"first_sub_checklist_answer\",\n", + " \"confidence\": 0.5, # Confidence scores should be added to the answer\n", " }\n", - " } \n", - " ] \n", + " }\n", + " ]\n", " }]\n", "}\n", "\n", @@ -274,14 +278,14 @@ " value=lb_types.Radio(\n", " answer=lb_types.ClassificationAnswer(\n", " name=\"first_radio_answer\",\n", - " confidence=0.5, # Confidence scores should be added to the answer \n", + " confidence=0.5, # Confidence scores should be added to the answer\n", " classifications=[\n", " lb_types.ClassificationAnnotation(\n", " name=\"sub_radio_question\",\n", " value=lb_types.Radio(\n", " answer=lb_types.ClassificationAnswer(\n", " name=\"first_sub_radio_answer\",\n", - " confidence=0.5 # Confidence scores should be added to the answer \n", + " confidence=0.5 # Confidence scores should be added to the answer\n", " )\n", " )\n", " )\n", @@ -298,7 +302,7 @@ " \"confidence\": 0.5,\n", " \"classifications\": [{\n", " \"name\":\"sub_radio_question\",\n", - " \"answer\": { \"name\" : \"first_sub_radio_answer\", \n", + " \"answer\": { \"name\" : \"first_sub_radio_answer\",\n", " \"confidence\": 0.5}\n", " }]\n", " }\n", @@ -312,7 +316,7 @@ { "metadata": {}, "source": [ - "############## Classification Free-form text ############## \n", + "############## Classification Free-form text ##############\n", "\n", "text_prediction = lb_types.ClassificationAnnotation(\n", " name=\"free_text\", # must match your ontology feature\"s name\n", @@ -344,9 +348,9 @@ "\n", "bbox_with_radio_subclass_prediction = lb_types.ObjectAnnotation(\n", " name=\"bbox_with_radio_subclass\",\n", - " confidence=0.5, \n", + " confidence=0.5,\n", " value=lb_types.DocumentRectangle(\n", - " start=lb_types.Point(x=bbox_dim[\"left\"], y=bbox_dim[\"top\"]), # x = left, y = top \n", + " start=lb_types.Point(x=bbox_dim[\"left\"], y=bbox_dim[\"top\"]), # x = left, y = top\n", " end=lb_types.Point(x=bbox_dim[\"left\"] + bbox_dim[\"width\"], y=bbox_dim[\"top\"] + bbox_dim[\"height\"]), # x= left + width , y = top + height\n", " unit=lb_types.RectangleUnit.POINTS,\n", " page=1\n", @@ -357,7 +361,7 @@ " \t\tvalue=lb_types.Radio(\n", " answer=lb_types.ClassificationAnswer(\n", " name=\"first_sub_radio_answer\",\n", - " confidence=0.5, \n", + " confidence=0.5,\n", " classifications=[\n", " lb_types.ClassificationAnnotation(\n", " name=\"second_sub_radio_question\",\n", @@ -379,13 +383,13 @@ " \"name\": \"bbox_with_radio_subclass\",\n", " \"classifications\": [\n", " {\n", - " \"name\": \"sub_radio_question\", \n", + " \"name\": \"sub_radio_question\",\n", " \"answer\": {\n", - " \"name\": \"first_sub_radio_answer\", \n", + " \"name\": \"first_sub_radio_answer\",\n", " \"confidence\": 0.5,\n", " \"classifications\": [\n", " {\n", - " \"name\": \"second_sub_radio_question\", \n", + " \"name\": \"second_sub_radio_question\",\n", " \"answer\": {\n", " \"name\": \"second_sub_radio_answer\", \"confidence\": 0.5}\n", " }\n", @@ -406,12 +410,12 @@ "metadata": {}, "source": [ "\n", - "############ NER with nested classifications ######## \n", + "############ NER with nested classifications ########\n", "\n", "\n", "ner_with_checklist_subclass_prediction = lb_types.ObjectAnnotation(\n", " name=\"ner_with_checklist_subclass\",\n", - " confidence=0.5, \n", + " confidence=0.5,\n", " value=lb_types.DocumentEntity(\n", " name=\"ner_with_checklist_subclass\",\n", " text_selections=[\n", @@ -438,7 +442,7 @@ " \"classifications\":[\n", " {\n", " \"name\": \"sub_checklist_question\",\n", - " \"answer\": [{\"name\": \"first_sub_checklist_answer\", \"confidence\":0.5 }] \n", + " \"answer\": [{\"name\": \"first_sub_checklist_answer\", \"confidence\":0.5 }]\n", " }\n", " ],\n", " \"textSelections\": [\n", @@ -449,9 +453,9 @@ " \"groupId\": \"\",\n", " \"page\": 1\n", " }\n", - " ] \n", + " ]\n", "}\n", - " \n" + "\n" ], "cell_type": "code", "outputs": [], @@ -467,12 +471,27 @@ { "metadata": {}, "source": [ - "## Text layer url is required for uploading entity annotations\n", + "Passing a `text_layer_url` is not longer required. Labelbox automatically generates a text layer using Google Document AI and its OCR engine to detect tokens. \n", + "\n", + "However, it's important to note that Google Document AI imposes specific restrictions on document size:\n", + "- The document must have no more than 15 pages.\n", + "- The file size should not exceed 20 MB.\n", + "\n", + "Furthermore, Google Document AI optimizes documents before OCR processing. This optimization might include rotating images or pages to ensure that text appears horizontally. Consequently, token coordinates are calculated based on the rotated/optimized images, resulting in potential discrepancies with the original PDF document.\n", + "\n", + "For example, in a landscape-oriented PDF, the document is rotated by 90 degrees before processing. As a result, all tokens in the text layer are also rotated by 90 degrees.\n", + "\n", + "You may still pass a `text_layer_url` if you wish to bypass the automatic text layer generation" + ], + "cell_type": "markdown" + }, + { + "metadata": {}, + "source": [ "global_key = \"0801.3483.pdf\"\n", "img_url = {\n", " \"row_data\": {\n", - " \"pdf_url\": \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483.pdf\",\n", - " \"text_layer_url\": \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483-lb-textlayer.json\"\n", + " \"pdf_url\": \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483.pdf\"\n", " },\n", " \"global_key\": global_key\n", "}\n", @@ -481,8 +500,15 @@ "dataset = client.create_dataset(name=\"pdf_demo_dataset\")\n", "task = dataset.create_data_rows([img_url])\n", "task.wait_till_done()\n", - "print(\"Errors:\",task.errors)\n", - "print(\"Failed data rows:\", task.failed_data_rows)" + "print(f\"Failed data rows: {task.failed_data_rows}\")\n", + "print(f\"Errors: {task.errors}\")\n", + "\n", + "if task.errors:\n", + " for error in task.errors:\n", + " if 'Duplicate global key' in error['message'] and dataset.row_count == 0:\n", + " # If the global key already exists in the workspace the dataset will be created empty, so we can delete it.\n", + " print(f\"Deleting empty dataset: {dataset}\")\n", + " dataset.delete()" ], "cell_type": "code", "outputs": [], @@ -503,9 +529,9 @@ "\n", "ontology_builder = lb.OntologyBuilder(\n", " classifications=[ # List of Classification objects\n", - " lb.Classification( \n", + " lb.Classification(\n", " class_type=lb.Classification.Type.RADIO,\n", - " name=\"radio_question\", \n", + " name=\"radio_question\",\n", " scope = lb.Classification.Scope.GLOBAL,\n", " options=[\n", " lb.Option(value=\"first_radio_answer\"),\n", @@ -514,13 +540,13 @@ " ),\n", " lb.Classification(\n", " class_type=lb.Classification.Type.CHECKLIST,\n", - " name=\"checklist_question\", \n", + " name=\"checklist_question\",\n", " scope = lb.Classification.Scope.GLOBAL,\n", " options=[\n", " lb.Option(value=\"first_checklist_answer\"),\n", " lb.Option(value=\"second_checklist_answer\")\n", " ]\n", - " ), \n", + " ),\n", " lb.Classification(\n", " class_type=lb.Classification.Type.TEXT,\n", " name=\"free_text\",\n", @@ -550,15 +576,15 @@ " options=[\n", " lb.Classification(\n", " class_type=lb.Classification.Type.CHECKLIST,\n", - " name=\"sub_checklist_question\", \n", + " name=\"sub_checklist_question\",\n", " options=[lb.Option(\"first_sub_checklist_answer\")]\n", " )\n", " ])\n", " ]\n", - " ), \n", + " ),\n", " ],\n", " tools=[ # List of Tool objects\n", - " lb.Tool( tool=lb.Tool.Type.BBOX,name=\"bounding_box\"), \n", + " lb.Tool( tool=lb.Tool.Type.BBOX,name=\"bounding_box\"),\n", " lb.Tool(tool=lb.Tool.Type.NER, name=\"named_entity\"),\n", " lb.Tool(tool=lb.Tool.Type.NER,\n", " name=\"ner_with_checklist_subclass\",\n", @@ -610,7 +636,7 @@ "metadata": {}, "source": [ "# create Model\n", - "model = client.create_model(name=\"PDF_model_run_\"+ str(uuid.uuid4()), \n", + "model = client.create_model(name=\"PDF_model_run_\"+ str(uuid.uuid4()),\n", " ontology_id=ontology.uid)\n", "# create Model Run\n", "model_run = model.create_model_run(\"iteration 1\")" @@ -650,19 +676,38 @@ { "metadata": {}, "source": [ - "First, we need to populate the text selections for Entity annotations\n", - "To learn how to generate a text layer for your documents please refer to the following repositories/files: \n", - "https://github.com/Labelbox/PDF-OCR-Transform-CLI/blob/main/src/scripts/gcloud/gcp-vision-to-lb-text-layer.py\n", - "https://github.com/Labelbox/PDF-OCR-Transform-CLI/blob/main/src/scripts/adobe/adobe-ocr-to-lb-text-layer.py" + "To import ner annotations, you must pass a `text_layer_url`, Labelbox automatically generates a `text_layer_url` after importing a pdf asset that doesn't include a `text_layer_url`" ], "cell_type": "markdown" }, { "metadata": {}, "source": [ - "import requests\n", - "import json\n", + "To extract the generated text layer url we first need to export the data row" + ], + "cell_type": "markdown" + }, + { + "metadata": {}, + "source": [ + "client.enable_experimental = True\n", + "task = lb.DataRow.export(client=client,global_keys=[global_key])\n", + "task.wait_till_done()\n", + "stream = task.get_stream()\n", "\n", + "text_layer = \"\"\n", + "for output in stream:\n", + " output_json = json.loads(output.json_str)\n", + " text_layer = output_json['media_attributes']['text_layer_url']\n", + "print(text_layer)" + ], + "cell_type": "code", + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "source": [ "# Helper method\n", "def update_text_selections(annotation, group_id, list_tokens, page):\n", " return annotation.update({\n", @@ -674,15 +719,14 @@ " }\n", " ]\n", " })\n", - " \n", "\n", - "text_layer = \"https://storage.googleapis.com/labelbox-datasets/arxiv-pdf/data/99-word-token-pdfs/0801.3483-lb-textlayer.json\"\n", "\n", "# Fetch the content of the text layer\n", - "res = requests.get(text_layer) \n", + "res = requests.get(text_layer)\n", "\n", "# Phrases that we want to annotation obtained from the text layer url\n", - "content_phrases = [\"Metal-insulator (MI) transitions have been one of the\", \"T. Sasaki,* N. Yoneyama, and N. Kobayashi\"]\n", + "content_phrases = [\"Metal-insulator (MI) transitions have been one of the\",\n", + " \"T. Sasaki, N. Yoneyama, and N. Kobayashi\"]\n", "\n", "# Parse the text layer\n", "text_selections = []\n", @@ -698,7 +742,7 @@ " text_selections.append(document_text_selection)\n", " # build text selection for the NDJson annotations\n", " update_text_selections(annotation=entities_prediction_ndjson,\n", - " group_id=group[\"id\"], # id representing group of words \n", + " group_id=group[\"id\"], # id representing group of words\n", " list_tokens=list_tokens, # ids representing individual words from the group\n", " page=1)\n", " if group[\"content\"] == content_phrases[1]:\n", @@ -708,17 +752,17 @@ " text_selections_ner.append(ner_text_selection)\n", " # build text selection for the NDJson annotations\n", " update_text_selections(annotation=ner_with_checklist_subclass_prediction_ndjson,\n", - " group_id=group[\"id\"], # id representing group of words \n", + " group_id=group[\"id\"], # id representing group of words\n", " list_tokens=list_tokens_2, # ids representing individual words from the group\n", " page=1)\n", "\n", "\n", - "#re-write the entity annotation with text selections \n", + "#re-write the entity annotation with text selections\n", "entities_prediction_document_entity = lb_types.DocumentEntity(name=\"named_entity\",confidence=0.5, textSelections = text_selections)\n", "entities_prediction = lb_types.ObjectAnnotation(name=\"named_entity\",value=entities_prediction_document_entity)\n", "\n", "\n", - "# re-write the entity annotation + subclassification with text selections \n", + "# re-write the entity annotation + subclassification with text selections\n", "classifications = [\n", " lb_types.ClassificationAnnotation(\n", " name=\"sub_checklist_question\",\n", @@ -728,12 +772,12 @@ " )\n", " ]\n", "ner_annotation_with_subclass = lb_types.DocumentEntity(name=\"ner_with_checklist_subclass\",confidence=0.5, textSelections= text_selections_ner)\n", - "ner_with_checklist_subclass_annotation = lb_types.ObjectAnnotation(name=\"ner_with_checklist_subclass\", \n", + "ner_with_checklist_subclass_annotation = lb_types.ObjectAnnotation(name=\"ner_with_checklist_subclass\",\n", " confidence=0.5,\n", - " value=ner_annotation_with_subclass, \n", + " value=ner_annotation_with_subclass,\n", " classifications=classifications)\n", "\n", - "# Final NDJSON and python annotations \n", + "# Final NDJSON and python annotations\n", "print(f\"entities_annotations_ndjson={entities_prediction_ndjson}\")\n", "print(f\"entities_annotation={entities_prediction}\")\n", "print(f\"nested_entities_annotation_ndjson={ner_with_checklist_subclass_prediction_ndjson}\")\n", @@ -762,7 +806,7 @@ " annotations = [\n", " entities_prediction,\n", " checklist_prediction,\n", - " nested_checklist_prediction, \n", + " nested_checklist_prediction,\n", " text_prediction,\n", " radio_prediction,\n", " nested_radio_prediction,\n", @@ -851,7 +895,7 @@ { "metadata": {}, "source": [ - "project = client.create_project(name=\"Document Prediction Import Demo\", \n", + "project = client.create_project(name=\"Document Prediction Import Demo\",\n", " media_type=lb.MediaType.Document)\n", "project.setup_editor(ontology)" ], @@ -899,7 +943,7 @@ "\n", "radio_annotation = lb_types.ClassificationAnnotation(\n", " name=\"radio_question\",\n", - " value=lb_types.Radio(answer = \n", + " value=lb_types.Radio(answer =\n", " lb_types.ClassificationAnswer(name = \"first_radio_answer\")\n", " )\n", ")\n", @@ -908,7 +952,7 @@ " name=\"checklist_question\",\n", " value=lb_types.Checklist(answer = [\n", " lb_types.ClassificationAnswer(name = \"first_checklist_answer\"),\n", - " lb_types.ClassificationAnswer(name = \"second_checklist_answer\"), \n", + " lb_types.ClassificationAnswer(name = \"second_checklist_answer\"),\n", " ])\n", " )\n", "\n", @@ -921,7 +965,7 @@ "bbox_annotation = lb_types.ObjectAnnotation(\n", " name=\"bounding_box\", # must match your ontology feature\"s name\n", " value=lb_types.DocumentRectangle(\n", - " start=lb_types.Point(x=bbox_dim_1[\"left\"], y=bbox_dim_1[\"top\"]), # x = left, y = top \n", + " start=lb_types.Point(x=bbox_dim_1[\"left\"], y=bbox_dim_1[\"top\"]), # x = left, y = top\n", " end=lb_types.Point(x=bbox_dim_1[\"left\"] + bbox_dim_1[\"width\"], y=bbox_dim_1[\"top\"]+ bbox_dim_1[\"height\"]), # x= left + width , y = top + height\n", " page=0,\n", " unit=lb_types.RectangleUnit.POINTS\n", @@ -966,7 +1010,7 @@ ")\n", "\n", "text_annotation = lb_types.ClassificationAnnotation(\n", - " name=\"free_text\", \n", + " name=\"free_text\",\n", " value=lb_types.Text(answer=\"sample text\")\n", ")\n", "\n", @@ -979,9 +1023,9 @@ "\n", "bbox_with_radio_subclass_annotation = lb_types.ObjectAnnotation(\n", " name=\"bbox_with_radio_subclass\",\n", - " \n", + "\n", " value=lb_types.DocumentRectangle(\n", - " start=lb_types.Point(x=bbox_dim[\"left\"], y=bbox_dim[\"top\"]), # x = left, y = top \n", + " start=lb_types.Point(x=bbox_dim[\"left\"], y=bbox_dim[\"top\"]), # x = left, y = top\n", " end=lb_types.Point(x=bbox_dim[\"left\"] + bbox_dim[\"width\"], y=bbox_dim[\"top\"] + bbox_dim[\"height\"]), # x= left + width , y = top + height\n", " unit=lb_types.RectangleUnit.POINTS,\n", " page=1\n", @@ -992,11 +1036,11 @@ " \t\tvalue=lb_types.Radio(\n", " answer=lb_types.ClassificationAnswer(\n", " name=\"first_sub_radio_answer\",\n", - " \n", + "\n", " classifications=[\n", " lb_types.ClassificationAnnotation(\n", " name=\"second_sub_radio_question\",\n", - " \n", + "\n", " value=lb_types.Radio(\n", " answer=lb_types.ClassificationAnswer(\n", " name=\"second_sub_radio_answer\"\n", @@ -1049,7 +1093,7 @@ " annotations = [\n", " entities_annotation,\n", " checklist_annotation,\n", - " nested_checklist_annotation, \n", + " nested_checklist_annotation,\n", " text_annotation,\n", " radio_annotation,\n", " nested_radio_annotation,\n",