The encord.objects.LabelRowV2 class is a wrapper around the Encord label row data format. It provides a convenient way to read, create, and manipulate labels.

The source code for the Encord SDK is public.

View Label Details

The following code shows how to view details for label rows (data units). This example only prints the label_hash and the date labels were created, but more details are available. To see all available label details, see the LabelRowV2 SDK Reference.

# Import dependencies
from typing import List

from encord import EncordUserClient
from encord.objects import LabelRowV2

# Authentication
user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path="<private_key_path>"
)

label_rows: List[LabelRowV2] = project.list_label_rows_v2()

for label_row in label_rows:
    print(f"Label hash: {label_row.label_hash}")
    print(f"Label created at: {label_row.created_at}")

Label Basics

Key methods for working with labels in the SDK are initialise_labels() and save():

  • initialise_labels(): Downloads the existing label information. This method must be called before performing any label operations using the SDK.
  • save(): Saves any changes made to the labels back to the server. This method must be called after completing label operations to ensure updates are stored.

The following example demonstrates how to use these methods to add a bounding box to a single data unit (label row):

# Import dependencies
from typing import List

from encord import EncordUserClient
from encord.objects import LabelRowV2, Object, ObjectInstance, OntologyStructure
from encord.objects.coordinates import BoundingBoxCoordinates

# Instantiate Encord client by replacing <private_key_path> with the path to your private key
user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path="<private_key_path>"
)

# Specify the project. Replace <project_hash> with the hash of your Project
project = user_client.get_project("<project_hash>")

# For simplicity, get only the first label row
first_label_row: List[LabelRowV2] = project.list_label_rows_v2()[0]

# Download the existing labels 
first_label_row.initialise_labels()

# Get the first object instance in the label row
ontology_structure = first_label_row.ontology_structure

# Gets the object instance we want to edit the label for. This example uses the first
first_obj_instance = first_label_row.get_object_instances()[0]

# Specifies which Ontology object to change labels for.
my_object = ontology_structure.get_child_by_title(title="Flower")

# This example changes the location of a bounding box
first_obj_instance.set_for_frames(
    coordinates = BoundingBoxCoordinates(
        height=0.1474,
        width =0.1154,
        top_left_x=0.1097,
        top_left_y=0.3209
    ),
    manual_annotation=False, overwrite=True
)

# Link the object instance to the label row.
first_label_row.add_object_instance(first_obj_instance)

# Saves the newly added bounding box
first_label_row.save()

Create Object Labels

Single Image or Frame

Creating and saving object instances is essential when importing annotations into Encord. Specify the label shape, the coordinates, and which frames the shape should occur on. For modalities without frames such as single images, ensure that frames=0.

Multiple Frames

When working with videos, an object instance may appear in multiple frames. For example, when tracking a car across frames, create a single object instance and place it on each frame where it appears. If objects are unique to each frame, create a new instance for each frame.


Edit labels

Editing labels after they have been created involves the following steps:

The script below shows how to update the location of an object’s bounding box, and attribute values. Ensure you substitute the following values:

  • <private_key_path> with the full path to your private key.
  • <project_hash> with the name of the project you want to update labels in.
  • <file_name> with the name of the file / data unit you want to edit labels for.
  • <object_name> with the name of the object.
  • <attribute_name> with the name of the attribute.
  • <option_name> with the name of the radio button option of the attribute.
# Import dependencies
from typing import List

from encord import EncordUserClient
from encord.objects import LabelRowV2, Object, ObjectInstance, OntologyStructure
from encord.objects.coordinates import BoundingBoxCoordinates

# Instantiate Encord client by replacing <private_key_path> with the path to your private key
user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path="<private_key_path>"
)

# Specify the project. Replace <project_hash> with the hash of your Project
project = user_client.get_project("<project_hash>")

# Specify the label row you want to edit labels for. This example takes the first label row
# Replace the <file_name.mp4> with the name of the file you want to change labels for
first_label_row = project.list_label_rows_v2(
    data_title_eq="<file_name>"
)[0]

# Download the existing labels 
first_label_row.initialise_labels()

# Get the first object instance in the label row
ontology_structure = first_label_row.ontology_structure

# Gets the object instance we want to edit the label for. This example uses the first
first_obj_instance = first_label_row.get_object_instances()[0]

# Specifies which Ontology object to change labels for
# Replace <object_name> with the name of your object. For example 'Flower'
my_object = ontology_structure.get_child_by_title(title="<object_name>")

# Sets coordinates for the specified object instance
# This example changes the location of a bounding box
first_obj_instance.set_for_frames(
    coordinates = BoundingBoxCoordinates(
        height=0.1474,
        width =0.1154,
        top_left_x=0.1097,
        top_left_y=0.3209
    ),
    # Overwrites if annotations are already present
    manual_annotation=False, overwrite=True
)

# Selects a Radio button attribute
# Replace <attribute_name> with the name of the attribute you want to edit. For example 'Flower species'
my_attribute = my_object.get_child_by_title(title="<attribute_name>", type_=RadioAttribute)

# Selects the radio button option of the attribute
# Replace <option_name> with the name of the option. For example 'Sunflower' 
attribute_option = my_attribute.get_child_by_title(title="<option_name>", type_= Option)

# Sets the attribute to the attribute_option defined above
first_object_instance.set_answer(attribute_option, overwrite=True)

# Once you have updated labels and attributes, save your changes
first_label_row.save()

View Object Instances

The following example prints all object instance for a specified label row (data unit).

# Import dependencies
from typing import List

from encord import EncordUserClient
from encord.objects import LabelRowV2, Object, ObjectInstance

# Instantiate Encord client by replacing <private_key_path> with the path to your private key
user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path="<private_key_path>"
)

# Specify the project. Replace <project_hash> with the hash of your Project
project = user_client.get_project("<project_hash>")

# For simplicity, get only the first label row
first_label_row: List[LabelRowV2] = project.list_label_rows_v2()[0]

# Download the existing labels 
first_label_row.initialise_labels()

# Check the get_object_instances optional filters for when you have many different object/classification instances.
all_object_instances: List[
    ObjectInstance
] = first_label_row.get_object_instances()

# Print all object instances
for instance in all_object_instances:
    print(instance)

# Optionally verify certain conditions are true
assert all_object_instances[0] == box_object_instance
assert all_object_instances[0].get_annotation(frame=0).manual_annotation is True

Create Classifications


Add attribute

The following scripts assume that an object label called Whale has already been created. Each script retrieves the object instance and searches for the corresponding attribute within the Project’s Ontology. For radio buttons and checklists, the scripts also identify the relevant attribute options.

  • Replace <private_key_path> with the key to your private key for authentication.
  • Replace <project_hash> with the hash of your Project.
  • Replace Whale with the name of your object.
  • Replace the attributes and attribute answers to suit your needs.

Nested Attributes

Radio button attributes can be nested. The following script assumes that an object label called Whale has already been created. To learn how to create a new object instance see the section here.

Radio attributes can also be nested under classifications. See here to learn how to do this.

Nested Radio Attributes
# Import dependencies
from typing import List

from encord import EncordUserClient
from encord.objects import LabelRowV2, Classification, ClassificationInstance, Option

# Instantiate Encord client by replacing <private_key_path> with the path to your private key
user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path="<private_key_path>"
)

# Specify the Project. Replace <project_hash> with the hash of your Project
project = user_client.get_project("<project_hash>")

# For simplicity, get only the first label row
first_label_row: List[LabelRowV2] = project.list_label_rows_v2()[0]

# Download the existing labels 
first_label_row.initialise_labels()

# Get the ontology structure
ontology_structure: OntologyStructure = first_label_row.ontology_structure

# Find Object label
object_label = ontology_structure.get_child_by_title(
    title="Whale",
    type_=Object
)

# Create object instance. We assume only 1 label
object_instance = first_label_row.get_object_instances()[0]

# Find the attribute
object_attribute = object_label.get_child_by_title("Colour")

# Find the attribute option you want to set
attribute_option = object_attribute.get_child_by_title(
    title="Blue",
    type_=Option
)

# Set the attribute option
object_instance.set_answer(attribute_option)

# Assume that the following radio button classification exists in the Ontology.
nested_radio_ontology_attribute: Classification = (
    ontology_structure.get_child_by_title(
        title="Any Other Colours?", type_=RadioAttribute
    )
)

# Assume the following radio button nested attribute exists in the Ontology
nested_radio_attribute_option = object_label.get_child_by_title(
    title="Pink",
    type_=Option
)

# Save labels
first_label_row.save()

Relation Attributes

Relation attributes work the exact same way as text attributes, except that when an attribute is marked as Relation the string #relation is appended to the attribute name.

Learn more about relation attributes in Encord here.
Relation Attributes
# Import dependencies
from typing import List

from encord import EncordUserClient
from encord.objects import LabelRowV2, Classification, ClassificationInstance, Option

# Instantiate Encord client by replacing <private_key_path> with the path to your private key
user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path="<private_key_path>"
)

# Specify the Project. Replace <project_hash> with the hash of your Project
project = user_client.get_project("<project_hash>")

# For simplicity, get only the first label row
first_label_row: List[LabelRowV2] = project.list_label_rows_v2()[0]

# Download the existing labels 
first_label_row.initialise_labels()

# Get the Ontology structure
ontology_structure: OntologyStructure = first_label_row.ontology_structure

# Assume that the following text attribute exists in the Ontology.
object_label = ontology_structure.get_child_by_title(
    title="Whale",
    type_=Object
)

# Create object instance. We assume only 1 label
object_instance = first_label_row.get_object_instances()[0]

# Find the attribute name
attribute = object_label.get_child_by_title("Description #relation")

# Set the answer for the attribute
object_instance.set_answer(attribute=attribute, answer="This is a picture of a whale")

# Save labels
first_label_row.save()

Dynamic Attributes

Dynamic attributes are attributes for object instances where the answer can change in each frame. You can read more about them here.

The following example adds bounding boxes for the label Person to three frames. It then sets the dynamic attribute Position to Walking and Standing. Wherever you can set frames, you can either set a single integer, a Range, or a list of Ranges.

# Import dependencies
from typing import List

from encord import EncordUserClient
from encord.objects import LabelRowV2, Object, ObjectInstance, OntologyStructure
from encord.objects.coordinates import PolygonCoordinates, PointCoordinate

# Instantiate Encord client by replacing <private_key_path> with the path to your private key
user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path="<private_key_path>"
)

# Specify the project. Replace <project_hash> with the hash of your Project
project = user_client.get_project("<project_hash>")

# For simplicity, get only the first label row
first_label_row: LabelRowV2 = project.list_label_rows_v2()[0]

# Download the existing labels
first_label_row.initialise_labels()

# Find the object in the Ontology
person_ontology_object: Object = ontology_structure.get_child_by_title(
    "Person", type_=Object
)

# Find the dynamic attribute in the Ontology
position_attribute = person_ontology_object.get_child_by_title(
    title="Position",  # The options here are "Standing" or "Walking"
    type_=RadioAttribute,
)

# Coordinates for bounding boxes on frames 3, 4, and 5
coordinates_per_frame = {
    3: BoundingBoxCoordinates(
        height=0.5,
        width=0.5,
        top_left_x=0.2,
        top_left_y=0.2,
    ),
    4: BoundingBoxCoordinates(
        height=0.5,
        width=0.5,
        top_left_x=0.3,
        top_left_y=0.3,
    ),
    5: BoundingBoxCoordinates(
        height=0.5,
        width=0.5,
        top_left_x=0.4,
        top_left_y=0.4,
    ),
}

# Add the bounding box labels to the object instance for each frame
for frame_number, coordinates in coordinates_per_frame.items():
    box_object_instance.set_for_frames(
        coordinates=coordinates, frames=frame_number
    )

# Link the object instance to the label row
first_label_row.add_object_instance(box_object_instance)

# Assume the person is standing in frame 1
person_object_instance.set_answer(
    answer=position_attribute.get_child_by_title("Standing", type_=Option),
    frames=0,
)

# Assume the person is walking in frames 2 and 3
person_object_instance.set_answer(
    answer=position_attribute.get_child_by_title("Walking", type_=Option),
    frames=Range(start=1, end=3),
)

# Save labels
first_label_row.save()

Exporting Labels

Exporting labels is a crucial operation in Encord. The SDK provides a flexible and customizable approach to downloading label information, meaning there is no single method for exporting labels. For comprehensive details and various examples, refer to our export documentation here.