Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions docs/schema/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,22 @@ The `AutolabelModel` type used in `autolabel_metadata` fields has the following

```json
AutolabelModel {
"name": <str> -- Name of the model used for annotation. Can include version information.
"name": <str> -- Name of the model used for auto-labeling. Can include version information.
"score": <float> -- Label score for the annotation from this model (range: 0.0–1.0).
"uncertainty": <option[float]> -- Model-reported uncertainty for the annotation (range: 0.0–1.0). Lower values imply higher confidence.
}
```

#### `AutolabelMetadata`

The `AutolabelMetadata` type consists of a list of `AutolabelModel`, and it has the following structure:

```json
AutolabelMetadata {
"models": <list[AutolabelModel]> -- List of models used for auto-labeling.
}
```

#### `Indicators`

The `Indicators` represents the status of vehicle indicators:
Expand Down Expand Up @@ -231,7 +241,7 @@ sample_annotation {
"next": <str> -- Foreign key to the `SampleAnnotation` table associated with the next annotation in the sequence. Empty string `""` if this is the last annotation.
"prev": <str> -- Foreign key to the `SampleAnnotation` table associated with the previous annotation in the sequence. Empty string `""` if this is the first annotation.
"automatic_annotation": <bool> -- Indicates whether the annotation was automatically generated. Defaults to `false`.
"autolabel_metadata": <option[[AutolabelModel;N]]> -- List of models used for autolabeling. Required if `automatic_annotation` is `true`.
"autolabel_metadata": <option[AutolabelMetadata]> -- Metadata of models used in auto-labeling. Required if `automatic_annotation` is `true`.
}
```

Expand All @@ -258,7 +268,7 @@ sample_data {
"prev": <str> -- Foreign key to the `SampleData` table associated with the previous data in the sequence. Empty string `""` if this is the first data.
"is_valid": <bool> -- Indicates whether this data is valid. Defaults to `true`.
"info_filename": <option[str]> -- Relative path to metadata-blob file.
"autolabel_metadata": <option[[AutolabelModel;N]]> -- List of models used for autolabeling applied to this entire sample_data item (e.g., image or scan).
"autolabel_metadata": <option[AutolabelMetadata]> -- Metadata of models used for auto-labeling applied to this entire sample_data item (e.g., image or scan).
}
```

Expand Down Expand Up @@ -367,7 +377,7 @@ object_ann {
"orientation": <option[float]> -- Orientation of the arrow shape within the bounding box, in radians. Present only for categories where `has_orientation` is true (e.g., traffic light arrows).
"number": <option[int]> -- The digit displayed within the bounding box. Present only for categories where `has_number` is true (e.g., numeric traffic lights).
"automatic_annotation": <bool> -- Whether the annotation was automatically generated. Defaults to `false`.
"autolabel_metadata": <option[[AutolabelModel;N]]> -- List of models used for autolabeling. Required if `automatic_annotation` is `true`.
"autolabel_metadata": <option[AutolabelMetadata]> -- Metadata of models used in auto-labeling. Required if `automatic_annotation` is `true`.
}
```

Expand All @@ -384,7 +394,7 @@ surface_ann {
"category_token": <str> -- Foreign key to the `Category` table associated with the category of the surface.
"mask": <RLE> -- Run length encoding of instance mask.
"automatic_annotation": <bool> -- Whether the annotation was automatically generated. Defaults to `false`.
"autolabel_metadata": <option[[AutolabelModel;N]]> -- List of models used for autolabeling. Required if `automatic_annotation` is `true`.
"autolabel_metadata": <option[AutolabelMetadata]> -- Metadata of models used in auto-labeling. Required if `automatic_annotation` is `true`.
}
```

Expand Down
58 changes: 51 additions & 7 deletions t4_devkit/schema/tables/autolabel_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def to_autolabel_model(x: list[dict | AutolabelModel] | None) -> list[AutolabelM
"""Convert input to a list of AutolabelModel instances.

Args:
x (list[dict | AutolabelModel] | None): Input to convert. Can be None, a list of dicts, or a list of AutolabelModel instances.
x (list[dict | AutolabelModel] | None): Can be None or a list of [dicts or AutolabelModel] instances.

Returns:
list[AutolabelModel] | None: Converted list of AutolabelModel instances or None.
Expand All @@ -44,19 +44,63 @@ def to_autolabel_model(x: list[dict | AutolabelModel] | None) -> list[AutolabelM
raise TypeError("Input must be None or a list of [dicts or AutolabelModel] instances.")


@define
class AutolabelMetadata:
"""A dataclass to represent metadata of models used in auto-labeling.

Attributes:
models (list[AutolabelModel]): List of AutolabelModel instances.
"""

models: list[AutolabelModel] | None = field(
converter=AutolabelModel.to_autolabel_model,
validator=validators.optional(
validators.deep_iterable(validators.instance_of(AutolabelModel))
),
)

@staticmethod
def to_autolabel_metadata(
x: dict | AutolabelMetadata | list[dict | AutolabelModel] | None,
) -> AutolabelMetadata | None:
"""Convert input to an AutolabelMetadata instance.

Args:
x (dict | AutolabelMetadata | list[dict | AutolabelModel] | None):
Input to convert. Can be None, a dict, or an AutolabelMetadata instance.

Returns:
AutolabelMetadata | None: Converted AutolabelMetadata instance or None.
"""
if x is None:
return None
if isinstance(x, AutolabelMetadata):
return x
if isinstance(x, list):
return AutolabelMetadata(x)
if isinstance(x, dict):
return AutolabelMetadata(x.get("models", None))
raise TypeError(
"Input must be None, a dict, an AutolabelMetadata instance, or a list of [dicts or AutolabelModel] instances."
)


@define(slots=False)
class AutolabelMixin:
"""Mixin class for schema tables that use autolabel metadata with automatic annotation."""
"""Mixin class for schema tables that use autolabel metadata with automatic annotation.

Attributes:
automatic_annotation (bool, optional): Indicates whether the annotation is generated automatically.
autolabel_metadata (AutolabelMetadata | None, optional): Metadata of models used in auto-labeling.
"""

automatic_annotation: bool = field(
default=False, validator=validators.instance_of(bool), kw_only=True
)
autolabel_metadata: list[AutolabelModel] | None = field(
autolabel_metadata: AutolabelMetadata | None = field(
default=None,
converter=AutolabelModel.to_autolabel_model,
validator=validators.optional(
validators.deep_iterable(validators.instance_of(AutolabelModel))
),
converter=AutolabelMetadata.to_autolabel_metadata,
validator=validators.optional(validators.instance_of(AutolabelMetadata)),
kw_only=True,
)

Expand Down
12 changes: 5 additions & 7 deletions t4_devkit/schema/tables/sample_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from attrs import define, field, validators

from ..name import SchemaName
from .autolabel_metadata import AutolabelModel
from .autolabel_metadata import AutolabelMetadata
from .base import SchemaBase, impossible_empty
from .registry import SCHEMAS

Expand Down Expand Up @@ -86,7 +86,7 @@ class SampleData(SchemaBase):
Empty if start of scene.
is_valid (bool): True if this data is valid, else False. Invalid data should be ignored.
info_filename (str): Relative path to metainfo data-blob on disk.
autolabel_metadata (list[AutolabelModel] | None, optional): List of models used for autolabeling applied to this entire sample_data item (e.g., image or scan).
autolabel_metadata (AutolabelMetadata | None, optional): Metadata of models used for autolabeling applied to this entire sample_data item (e.g., image or scan).

Shortcuts:
---------
Expand All @@ -111,12 +111,10 @@ class SampleData(SchemaBase):
info_filename: str | None = field(
default=None, validator=validators.optional(validators.instance_of(str))
)
autolabel_metadata: list[AutolabelModel] | None = field(
autolabel_metadata: AutolabelMetadata | None = field(
default=None,
converter=AutolabelModel.to_autolabel_model,
validator=validators.optional(
validators.deep_iterable(validators.instance_of(AutolabelModel))
),
converter=AutolabelMetadata.to_autolabel_metadata,
validator=validators.optional(validators.instance_of(AutolabelMetadata)),
)

# shortcuts
Expand Down
20 changes: 12 additions & 8 deletions tests/schema/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ def sample_annotation_dict() -> dict:
"next": "7b0ae1dae7531b7b917f403cb22259e6",
"prev": "",
"automatic_annotation": True,
"autolabel_metadata": [{"name": "centerpoint_v1.2", "score": 0.95, "uncertainty": 0.1}],
"autolabel_metadata": {
"models": [{"name": "centerpoint_v1.2", "score": 0.95, "uncertainty": 0.1}]
},
}


Expand Down Expand Up @@ -233,9 +235,9 @@ def sample_data_dict() -> dict:
"is_valid": True,
"next": "efe096cc01a610af846c29aaf4decc9a",
"prev": "",
"autolabel_metadata": [
{"name": "image_preprocessor_v2.1", "score": 0.99, "uncertainty": None}
],
"autolabel_metadata": {
"models": [{"name": "image_preprocessor_v2.1", "score": 0.99, "uncertainty": None}]
},
}


Expand Down Expand Up @@ -340,10 +342,12 @@ def object_ann_dict() -> dict:
"bbox": [0, 408.0529355733727, 1920, 728.1832152454293],
"mask": {"size": [1920, 1280], "counts": "UFBQWzI='"},
"automatic_annotation": True,
"autolabel_metadata": [
{"name": "yolo_v8_segmentation", "score": 0.87, "uncertainty": 0.15},
{"name": "mask_rcnn_v3.0", "score": 0.92, "uncertainty": None},
],
"autolabel_metadata": {
"models": [
{"name": "yolo_v8_segmentation", "score": 0.87, "uncertainty": 0.15},
{"name": "mask_rcnn_v3.0", "score": 0.92, "uncertainty": None},
]
},
"number": None,
"orientation": None,
}
Expand Down
43 changes: 35 additions & 8 deletions tests/schema/tables/test_autolabel_metadata.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
from __future__ import annotations

import pytest
from t4_devkit.schema.tables.autolabel_metadata import AutolabelModel, AutolabelMixin

from t4_devkit.schema.tables.autolabel_metadata import (
AutolabelMetadata,
AutolabelMixin,
AutolabelModel,
)


class TestAutolabelModel:
Expand All @@ -9,27 +16,47 @@ def test_to_autolabel_model_type_error(self):
"""Test to_autolabel_model raises TypeError for invalid input."""
with pytest.raises(
TypeError,
match="Input must be None or a list of \\[dicts or AutolabelModel\\] instances.",
match="Input must be None or a list of \\[dicts or AutolabelModel\\] instances",
):
AutolabelModel.to_autolabel_model("invalid_input")

def test_to_autolabel_model_type_error_with_dict(self):
"""Test to_autolabel_model raises TypeError when input is a dict instead of list."""
with pytest.raises(
TypeError,
match="Input must be None or a list of \\[dicts or AutolabelModel\\] instances.",
match="Input must be None or a list of \\[dicts or AutolabelModel\\] instances",
):
AutolabelModel.to_autolabel_model({"name": "model1", "score": 0.8})

def test_to_autolabel_model_type_error_with_number(self):
"""Test to_autolabel_model raises TypeError when input is a number."""
with pytest.raises(
TypeError,
match="Input must be None or a list of \\[dicts or AutolabelModel\\] instances.",
match="Input must be None or a list of \\[dicts or AutolabelModel\\] instances",
):
AutolabelModel.to_autolabel_model(123)


class TestAutolabelMetadata:
"""Test cases for AutolabelMetadata class that are not covered elsewhere."""

def test_to_autolabel_metadata_type_error(self):
"""Test to_autolabel_metadata raises TypeError for invalid input."""
with pytest.raises(
TypeError,
match="Input must be None, a dict, an AutolabelMetadata instance, or a list of \\[dicts or AutolabelModel\\] instances.",
):
AutolabelMetadata.to_autolabel_metadata("invalid_input")

def test_to_autolabel_metadata_type_error_with_number(self):
"""Test to_autolabel_metadata raises TypeError when input is a number."""
with pytest.raises(
TypeError,
match="Input must be None, a dict, an AutolabelMetadata instance, or a list of \\[dicts or AutolabelModel\\] instances.",
):
AutolabelMetadata.to_autolabel_metadata(123)


class TestAutolabelMixin:
"""Test cases for AutolabelMixin class that are not covered elsewhere."""

Expand All @@ -42,16 +69,16 @@ def test_autolabel_mixin_error_automatic_true_no_metadata(self):

def test_autolabel_mixin_error_automatic_false_with_metadata(self):
"""Test AutolabelMixin raises TypeError when automatic_annotation=False but autolabel_metadata is provided."""
models = [AutolabelModel(name="test_model", score=0.8)]
metadata = AutolabelMetadata(models=[AutolabelModel(name="test_model", score=0.8)])
with pytest.raises(
TypeError, match="autolabel_metadata must be None when automatic_annotation is False"
):
AutolabelMixin(automatic_annotation=False, autolabel_metadata=models)
AutolabelMixin(automatic_annotation=False, autolabel_metadata=metadata)

def test_autolabel_mixin_error_default_automatic_with_metadata(self):
"""Test AutolabelMixin raises TypeError when default automatic_annotation=False but autolabel_metadata is provided."""
models = [AutolabelModel(name="test_model", score=0.8)]
metadata = AutolabelMetadata(models=[AutolabelModel(name="test_model", score=0.8)])
with pytest.raises(
TypeError, match="autolabel_metadata must be None when automatic_annotation is False"
):
AutolabelMixin(autolabel_metadata=models)
AutolabelMixin(autolabel_metadata=metadata)
Loading