diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e0a441e..e316379 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,28 +15,20 @@ jobs: - uses: actions/setup-python@v4 with: python-version: "3.12" + - name: Set up virtual environment + run: |- + python -m venv .venv + source .venv/bin/activate - name: Upgrade pip run: |- - python -m pip install -U pip + pip install -U pip + - name: Install packages + run: |- + pip install '.[test]' + ls .venv/bin - name: Install pre-commit run: |- - python -m pip install pre-commit types-docutils + pip install pre-commit types-docutils - name: Run Pre-Commit run: |- pre-commit run --all-files - pylint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 - with: - python-version: "3.12" - - name: Upgrade pip - run: |- - python -m pip install -U pip - - name: Install pylint - run: |- - python -m pip install pylint - - name: Run pylint - run: |- - pylint -dfixme raillabel || exit $(($? & ~24)) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5e2a002..1d07227 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,21 +25,27 @@ repos: - id: end-of-file-fixer - id: fix-byte-order-marker - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 24.10.0 - hooks: - - id: black - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.0 hooks: - - id: isort - - repo: https://github.com/PyCQA/pydocstyle - rev: 6.3.0 + - id: ruff-format + name: Run Formatter + - id: ruff + name: Run Linter + args: [ --fix ] + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.12.1 hooks: - - id: pydocstyle - exclude: '^tests/' - additional_dependencies: - - pydocstyle[toml] + - id: mypy + name: Run Type Checker + types_or: [python, pyi, toml, yaml] + pass_filenames: false + args: [raillabel] + additional_dependencies: + - "pydantic<3.0.0" + - repo: https://github.com/Lucas-C/pre-commit-hooks rev: v1.5.5 hooks: @@ -83,11 +89,13 @@ repos: - LICENSES/.license_header.txt - --comment-style - '..| |' + - repo: https://github.com/fsfe/reuse-tool rev: v4.0.3 hooks: - id: reuse + - repo: https://github.com/qoomon/git-conventional-commits - rev: v2.1.1 + rev: v2.6.7 hooks: - id: conventional-commits diff --git a/CHANGELOG.md b/CHANGELOG.md index b66ad8c..e7d5858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,7 @@ Release - ```frame_data``` can only contain ```Num``` instances - ```object_data``` can not contain ```Num``` instances anymore - Major restructuring of the project directories -- ```FrameInterval.from_frame_uids()```: create ```FrameIntervals``` by providing a list of frame uids +- ```FrameInterval.from_frame_ids()```: create ```FrameIntervals``` by providing a list of frame uids - ```Object.object_data_pointers()```: generate ```ElementDataPointers``` - ```Scene.frame_intervals()```, ```Object.frame_intervals()```: generate ```FrameIntervals``` - ```Object.asdict()``` now provides also frame intervals and object data pointers, if the frames from the scene are provided @@ -92,3 +92,25 @@ Release ## 3.3.0 - Introduce support for Python 3.13 + +# 4.0.0 +New Major changes to RailLabel! Over the time two major use cases for this package have crystalized. The first one are the users of the data sets published by Deutsche Bahn. The second one are the contractors providing the annotations. Since the two groups have vastly different requirements for this package, we decided to split it accordingly. + +If you just want to work with out data, then you are in luck. You can just continue using this package with even more focus on your needs. + +If you are building raillabel scenes yourself or want to manipulate the data in a safe way, then the [raillabel_providerkit](https://github.com/DSD-DBS/raillabel-providerkit) is for you. All functionality you may be missing in this new raillabel version will be provided over there with even better APIs. + +Functionality, that has been **moved** to the `raillabel_providerkit`: +- loading annotations in formats other than raillabel itself +- validating the content of files + +Other breaking changes: +- the `fromdict()` and `asdict()` methods in `raillabel.format` classes have been replaced with `from_json()` and `to_json` respectively +- `raillabel.format.FrameInterval` fields have been changed by `frame_start -> start` and `frame_end -> end` to make it more concise +- all uid fields of classes have been removed (like `raillabel.format.Frame.uid`) have been removed to avoid redundant information +- `raillabel.format.Sensor` has been removed in favor of the different sensor type classes `raillabel.format.Camera`, `raillabel.format.Lidar`, `raillabel.format.Radar` and `raillabel.format.GpsImu` +- `raillabel.filter()` has been removed in favor of `raillabel.Scene.filter()` with different input arguments + +New features: +- `raillabel.json_format` has been introduced as an interface between the JSON format and the `raillabel` classes +- `raillabel.scene_builder.SceneBuilder` is now available to easily build scenes for testing purposes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b57e49..e04361d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -138,7 +138,7 @@ The key differences are: etc. - For classes that are not builtin (e.g. `Iterable`), `import collections.abc as cabc` and then use them like `cabc.Iterable`. - - Use [PEP-604-style unions], e.g. `int | float` instead of + - Use [PEP-604-style unions], e.g. `float` instead of `t.Union[int, float]`. - Use `... | None` (with `None` always as the last union member) instead of `t.Optional[...]` and always explicitly annotate where `None` is possible. diff --git a/README.md b/README.md index 465eb59..3bb2b38 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,44 @@ pip install -e '.[docs,test]' pre-commit install ``` +# Usage + +The first step in using `raillabel` is downloading a desired dataset (like [OSDaR23](https://data.fid-move.de/dataset/osdar23)). You can then load any scene by running +```python +import raillabel + +scene = raillabel.load("path/to/annotation_file.json") +``` + +This returns a [`raillabel.Scene`](https://dsd-dbs.github.io/raillabel/code/raillabel.html#raillabel.Scene), which is the root class for the annotations. + +If a file is too extensive for your use-case you can filter out certain parts of a scene like this +```python +from raillabel.filter import ( + IncludeObjectTypeFilter, + ExcludeAnnotationTypeFilter, + StartTimeFilter, ExcludeFrameIdFilter, + IncludeAttributesFilter +) + +scene_with_only_trains = scene.filter([IncludeObjectTypeFilter(["rail_vehicle"])]) + +scene_without_bboxs = scene.filter([ExcludeAnnotationTypeFilter(["bbox"])]) + +cut_scene_with_only_red_trains = scene.filter([ + StartTimeFilter("1587349200.004200000"), + ExcludeFrameIdFilter([2, 4]), + IncludeObjectTypeFilter(["rail_vehicle"]), + IncludeAttributesFilter({"color": "red"}), +]) +``` +An overview of all available filters can be found [here](https://dsd-dbs.github.io/raillabel/code/raillabel.filter.html#module-raillabel.filter). + +If you then want to save your changes, then use +```python +raillabel.save(cut_scene_with_only_red_trains, "/path/to/target.json") +``` + # Contributing We'd love to see your bug reports and improvement suggestions! Please take a diff --git a/docs/source/conf.py b/docs/source/conf.py index 98f3cba..c5bda95 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -66,9 +66,7 @@ rst_epilog = """ .. |Project| replace:: {project} .. |Version| replace:: {version} -""".format( - project=project, version=version -) +""".format(project=project, version=version) # -- Options for copy-button ------------------------------------------------- diff --git a/docs/source/howto.rst b/docs/source/howto.rst deleted file mode 100644 index 5810e32..0000000 --- a/docs/source/howto.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. - Copyright DB InfraGO AG and contributors - SPDX-License-Identifier: Apache-2.0 - -.. _howtos: - -******* -How Tos -******* - -In this section you can view dedicated tutorial-notebooks of key -features. - - -.. toctree:: - :maxdepth: 4 - :caption: How tos: - :glob: - - howtos/* diff --git a/docs/source/howtos/1 Validating Annotation Files.rst b/docs/source/howtos/1 Validating Annotation Files.rst deleted file mode 100644 index c87ef96..0000000 --- a/docs/source/howtos/1 Validating Annotation Files.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. - Copyright DB InfraGO AG and contributors - SPDX-License-Identifier: Apache-2.0 - -============================= -1 Validating Annotation Files -============================= - -Some annotation files might not fit the valid schema, depending on their source and editing history. To check whether a JSON file is a valid RailLabel annotation file, the raillabel.validate() method can be used. This checks the JSON file against a given schema and returns whether the data is valid and any potential incompatibilities with the schema. - -.. code-block:: python - - import raillabel - - valid_data = { - "openlabel": { - "metadata": { - "schema_version": "1.0.0" - } - } - } - - raillabel.validate(valid_data) - -.. code-block:: python - - Returns: (True, []) - -.. code-block:: python - - import raillabel - - invalid_data = { - "openlabel": { - "metadata": { - "schema_version": "1.0.0" - }, - "invalid_field": "foo" - } - } - - raillabel.validate(invalid_data) - -.. code-block:: python - - Returns: (False, ["$.openlabel: Additional properties are not allowed ('invalid_field' was unexpected)"]) - -By default, the OpenLABEL JSON schema is used for validation. However, the method can validate data with any schema. - -.. code-block:: python - - import raillabel - - data = { - "openlabel": { - "metadata": { - "schema_version": "2.0.0" - } - } - } - - raillabel.validate(data, "path/to/schema.json") - -The method returns a tuple of two elements. The first element is a boolean marking if the data validates against the schema. The second is a list of strings with each string being an error in the data. This example shows how to present the results of the method to a user. - -.. code-block:: python - - is_data_valid, warnings = raillabel.validate(data) - - if is_data_valid: - do_something() - - else: - for w in warnings: - print(w) diff --git a/docs/source/howtos/2 Loading Annotation Files.rst b/docs/source/howtos/2 Loading Annotation Files.rst deleted file mode 100644 index 0d65c3d..0000000 --- a/docs/source/howtos/2 Loading Annotation Files.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. - Copyright DB InfraGO AG and contributors - SPDX-License-Identifier: Apache-2.0 - -========================== -2 Loading Annotation Files -========================== - -Loading annotation files into a raillabel.Scene is done with ``raillabel.load()``. - -.. code-block:: python - - import raillabel - scene = raillabel.load('path/to/file.json') - -The method can itself identify if the file is from a supported format and then choose the correct loader-class. - -Validation -========== -If the data should be validated before beeing loaded, the ``validate`` parameter can be set to True. If the data is not valid, a ``raillabel.SchemaError`` is raised with all errors included. - -.. code-block:: python - - import raillabel - scene = raillabel.load('path/to/file.json', validate=True) diff --git a/docs/source/howtos/3 Saving Annotation Files.rst b/docs/source/howtos/3 Saving Annotation Files.rst deleted file mode 100644 index dce3d7f..0000000 --- a/docs/source/howtos/3 Saving Annotation Files.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. - Copyright DB InfraGO AG and contributors - SPDX-License-Identifier: Apache-2.0 - -========================= -3 Saving Annotation Files -========================= - -If changes have been made to an annotation file or a file should be copied, it can be saved via the ``raillabel.save()`` method. - -.. code-block:: python - - import raillabel - - scene = raillabel.load('path/to/file.json') - scene.frames['0'].stream_stamps['lidar'].timestamp += 37 - raillabel.save(scene, 'path/to/file.json') - -By default, the JSON file is saved as compact as possible with no linebreaks or indents. This can be changed by setting ``prettify_json=True`` as an argument. The indent size used is 4 spaces. - -Validation -========== - -If the data should be validated after beeing saved, the ``validate`` parameter can be set to True. If the data is not valid, a ``raillabel.SchemaError`` is raised with all errors included and the data is not saved. - -.. code-block:: python - - import raillabel - - scene = raillabel.load('path/to/file.json') - scene.frames['0'].stream_stamps['lidar'].timestamp += 37 - raillabel.save(scene, 'path/to/file.json', validate=True) diff --git a/docs/source/howtos/4 Filtering Scenes.rst b/docs/source/howtos/4 Filtering Scenes.rst deleted file mode 100644 index 4b99330..0000000 --- a/docs/source/howtos/4 Filtering Scenes.rst +++ /dev/null @@ -1,50 +0,0 @@ -.. - Copyright DB InfraGO AG and contributors - SPDX-License-Identifier: Apache-2.0 - -================== -4 Filtering Scenes -================== - -The annotations of a scene can be filtered by many criteria. This functionality is provided by ``raillabel.filter()``. This method takes a scene and filter arguments and returns a copy of the scene with filters applied. - -.. code-block:: python - - import raillabel - import decimal - - scene = raillabel.load('path/to/file.json') - - scene_with_only_trains = raillabel.filter( - scene, - include_object_types=['train'] - ) - scene_without_bboxs = raillabel.filter( - scene, - exclude_annotation_types=['bbox'] - ) - cut_scene_with_only_red_trains = raillabel.filter( - scene, - start_timestamp=decimal.Decimal('1587349200.004200000'), - exclude_frames=[4, 2], - include_object_types=['train'], - include_attributes={ - 'color': 'red' - } - ) - scene_with_annotations_with_an_attribute = raillabel.filter( - scene, - include_attributes={ # All annotations with the color - 'color': None # attribute will be included, - } # regardless of color value. - ) - -Most filter categories have an include and exclude parameter (i.e ``include_object_types`` and ``exclude_object_types``). When include is set, all annotations, that meet the criterium are *included* into the filtered scene. Excluded parameters are *excluded* from the scene. These two parameters are mutually exclusive and can not both be set. - -.. code-block:: python - - invalid_scene = raillabel.filter( - scene, - include_object_types=['person'], # Will raise a ValueError due - exclude_object_types=['train'] # to mutual exclusivity. - ) diff --git a/docs/source/index.rst b/docs/source/index.rst index 46fb152..a1d914a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,6 +15,11 @@ Python RailLabel Development-Kit **Date**: |today| **Version**: |Version| +.. + Copyright DB InfraGO AG and contributors + SPDX-License-Identifier: Apache-2.0 + + Description ----------- This library is designed to assist in the handling of the sensor annotations of Deutsche Bahn in the OpenLABEL data format. Common usage for the library: @@ -23,20 +28,37 @@ This library is designed to assist in the handling of the sensor annotations of * filtering for specific frames, annotations or objects * editing annotation files -If you want a quickstart at how to use this package, head right into the -:ref:`how-tos section `. +Motivation +---------- -.. toctree:: - :caption: Introduction - :maxdepth: 4 +Working with our own data has brought up the need to interact with the annotations programmatically. The annotation data is stored in .json files in the `ASAM OpenLABEL annotation `_ format, an emerging industry standard targeted towards the automotive sector. But as a standard it is designed very inclusively, which makes it overloaded for our limited use cases. We therefore decided to create a submodel called "RailLabel" with a corresponding devkit for easier interaction with the data. The example below shows a comparison between a purely JSON based approach compared to the devkit. - intro +With JSON only: -.. toctree:: - :caption: Tutorials - :titlesonly: +.. code-block:: python + + import json + + with open('path/to/file.json', 'r') as data_file: + scene = json.load(data_file) + + scene['openlabel']['frames']['1']['frame_properties']['streams']['lidar']['stream_properties']['stream_sync']['timestamp'] += 37 + + with open('path/to/other_file.json', 'w') as data_file: + json.dump(scene, data_file) + +With RailLabel: + +.. code-block:: python + + import raillabel + + scene = raillabel.load('path/to/file.json') + scene.frames[1].sensors['lidar'].timestamp += 37 + raillabel.save(scene, 'path/to/other_file.json') - howto +Content +------- .. toctree:: :caption: Modules diff --git a/pyproject.toml b/pyproject.toml index e38d9a8..a133dbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,11 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 [build-system] -requires = [ - "setuptools>=64", - "setuptools_scm[toml]>=3.4", - "wheel" -] +requires = ["setuptools>=64", "setuptools_scm[toml]>=3.4", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -17,9 +13,7 @@ description = "A devkit for working with recorded and annotated train ride data readme = "README.md" requires-python = ">=3.8, <3.14" license = { text = "Apache-2.0" } -authors = [ - { name = "DB InfraGO AG" }, -] +authors = [{ name = "DB InfraGO AG" }] keywords = [] classifiers = [ "Development Status :: 1 - Planning", @@ -35,9 +29,8 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] dependencies = [ - "jsonschema>=4.4.0", - "fastjsonschema>=2.16.2", - "typing_extensions==4.12.2" + "pydantic<3.0.0", + "eval-type-backport==0.2.0" ] [project.urls] @@ -45,31 +38,48 @@ Homepage = "https://github.com/DSD-DBS/raillabel" Documentation = "https://dsd-dbs.github.io/raillabel" [project.optional-dependencies] -docs = [ - "furo", - "sphinx", - "sphinx-copybutton", - "tomli; python_version<'3.11'", -] +docs = ["furo", "sphinx", "sphinx-copybutton", "tomli; python_version<'3.14'"] -test = [ - "pytest", - "pytest-cov", - "json5" -] +test = ["pytest", "pytest-cov", "json5"] + +[tool.ruff] +line-length = 101 + +[tool.ruff.lint] +exclude = ["tests/*", "docs/*"] +select = ["ALL"] +ignore = [ + "A002", # objects from OpenLABEL conflict with a Python builtin, but staying consistent with the domain is more important + + "B024", # needed for _FilterAbc + + "COM812", # conflicts with ruff formatter + + "D100", # imo no docstrings are necessary in public modules + "D107", # __init__ docstrings are not necessary + "D203", # incompatible with D211 + "D213", # incompatible with D212 + "D413", # rule by convention which looks weird + "D417", # kwargs can not be typed for the filter function + + "FBT001", # flags in functions are not bad practice + "FBT002", # flags in functions are not bad practice + + "INP001", # load and save are not meant as packages and are only folders for consistency + + "ISC001", # conflicts with ruff formatter -[tool.black] -line-length = 100 -target-version = ["py312"] -force-exclude = "tests/" + "N802", # does not allow constant abstractproperties -[tool.docformatter] -wrap-descriptions = 72 -wrap-summaries = 79 + "TCH001", # adds hard to understand compexity without providing a benefit for smaller projects + "TCH002", # same as TCH001 + "TCH003", # same as TCH001 -[tool.isort] -profile = 'black' -line_length = 100 + "SIM103", # less readable in some cases imo + + # Should be removed in the future + "PGH003", +] [tool.mypy] check_untyped_defs = true @@ -78,95 +88,10 @@ show_error_codes = true warn_redundant_casts = true warn_unreachable = true python_version = "3.12" - -[[tool.mypy.overrides]] -module = ["tests.*"] -allow_incomplete_defs = true -allow_untyped_defs = true - -[[tool.mypy.overrides]] -# Untyped third party libraries -module = [ - # ... -] +disable_error_code = ["override"] +plugins = "pydantic.mypy" ignore_missing_imports = true -[tool.pydocstyle] -convention = "numpy" -add-select = [ - "D212", # Multi-line docstring summary should start at the first line - "D402", # First line should not be the functions "signature" - "D417", # Missing argument descriptions in the docstring -] -add-ignore = [ - "D100", # Missing docstring in public module - "D201", # No blank lines allowed before function docstring # auto-formatting - "D202", # No blank lines allowed after function docstring # auto-formatting - "D203", # 1 blank line required before class docstring # auto-formatting - "D204", # 1 blank line required after class docstring # auto-formatting - "D209", # Multi-line docstring closing quotes should be on a separate line - "D211", # No blank lines allowed before class docstring # auto-formatting - "D213", # Multi-line docstring summary should start at the second line -] - -[tool.pylint.master] -max-line-length = 100 - -[tool.pylint.messages_control] -disable = [ - "arguments-renamed", - "global-statement", - "invalid-name", - "no-else-return", # using else returns is more readible imo - "protected-access", # class comparisons raised as false positive - "redefined-builtin", # the domain is full of builtin-names (object, type, format, ...) - "too-few-public-methods", # does not contribute to code quality imo - "too-many-arguments", # 6 as a limit is too low - "too-many-instance-attributes", # classes mirror OpenLABEL, therefore the number of fields is set - "unidiomatic-typecheck", # type() is necessary in some cases - "unspecified-encoding", # default encoding is sufficient in all cases - "unsupported-membership-test", # raise false positives for dicts - "global-variable-not-assigned", # raises false positive when global variable is a dict and items are assigned - - # Auto-formatting - "bad-indentation", - "inconsistent-quotes", - "missing-final-newline", - "missing-class-docstring", - "missing-function-docstring", - "missing-module-docstring", - "mixed-line-endings", - "multiple-imports", - "multiple-statements", - "trailing-newlines", - "trailing-whitespace", - "unexpected-line-ending-format", - "ungrouped-imports", - "wrong-import-order", - "wrong-import-position", - - # Handled by mypy - "arguments-differ", - "assignment-from-no-return", - "import-error", - "missing-kwoa", - "no-member", - "no-value-for-parameter", - "redundant-keyword-arg", - "signature-differs", - "syntax-error", - "too-many-function-args", - "unbalanced-tuple-unpacking", - "undefined-variable", - "unexpected-keyword-arg", -] -enable = [ - "c-extension-no-member", - "deprecated-pragma", - "use-symbolic-message-instead", - "useless-suppression", -] - [tool.pytest.ini_options] addopts = """ --strict-config diff --git a/raillabel/__init__.py b/raillabel/__init__.py index 40dfb0a..344419b 100644 --- a/raillabel/__init__.py +++ b/raillabel/__init__.py @@ -1,15 +1,21 @@ # Copyright DB InfraGO AG and contributors # SPDX-License-Identifier: Apache-2.0 """Devkit for working with recorded and annotated train ride data from DB.""" + from importlib import metadata -from . import _util, format, stats -from .exceptions import * -from .filter.filter import filter +from . import filter, format from .format import Scene -from .load_.load import load +from .load.load import load from .save.save import save -from .validate.validate import validate + +__all__ = [ + "filter", + "format", + "Scene", + "load", + "save", +] try: __version__ = metadata.version("raillabel") diff --git a/raillabel/_util/_attribute_type.py b/raillabel/_util/_attribute_type.py deleted file mode 100644 index 3fff392..0000000 --- a/raillabel/_util/_attribute_type.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from enum import Enum - - -class AttributeType(Enum): - """Enum of all valid RailLabel attribute types.""" - - TEXT = "text" - NUM = "num" - BOOLEAN = "boolean" - VEC = "vec" - - @classmethod - def from_value(cls, attribute_value_class: t.Type) -> "AttributeType": - """Return AttributeType based on class of attribute value. - - Parameters - ---------- - attribute_value_class: type - Class of the attribute value. Can be gathered by calling type()-function. - - Returns - ------- - AttributeType - Corresponding AttributeType. - - Raises - ------ - ValueError - if attribute value class does not correspond to an Attribute Type. - """ - - if attribute_value_class == str: - return AttributeType.TEXT - - elif attribute_value_class in [float, int]: - return AttributeType.NUM - - elif attribute_value_class == bool: - return AttributeType.BOOLEAN - - elif attribute_value_class in [list, tuple]: - return AttributeType.VEC - - else: - raise ValueError( - f"Type {attribute_value_class} does not correspond to a valid RailLabel attribute " - + "type. Supported types are str, float, int, bool, list, tuple." - ) diff --git a/raillabel/_util/_clean_dict.py b/raillabel/_util/_clean_dict.py deleted file mode 100644 index 77d5444..0000000 --- a/raillabel/_util/_clean_dict.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - - -def _clean_dict(input_dict: dict) -> dict: - """Remove all fields from a dict that are None or have a length of 0.""" - - empty_keys = [] - for key, value in input_dict.items(): - - is_field_empty = value is None or (hasattr(value, "__len__") and len(value) == 0) - - if is_field_empty: - empty_keys.append(key) - - for key in empty_keys: - del input_dict[key] - - return input_dict diff --git a/raillabel/_util/_warning.py b/raillabel/_util/_warning.py deleted file mode 100644 index 828b1b3..0000000 --- a/raillabel/_util/_warning.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import logging -import typing as t -from io import StringIO - - -class _WarningsLogger: - - warnings: t.List[str] = [] - - def __enter__(self) -> "_WarningsLogger": - logger = logging.getLogger("loader_warnings") - warnings_stream = StringIO() - handler = logging.StreamHandler(warnings_stream) - handler.setLevel(logging.WARNING) - logger.addHandler(handler) - - return self - - def __exit__(self, exc_type, exc_value, traceback): - logger = logging.getLogger("loader_warnings") - stream = logger.handlers[-1].stream - stream.seek(0) - - warnings_list = stream.getvalue().split("\n") - - if len(warnings_list) > 0: - warnings_list = warnings_list[:-1] - - self.warnings = warnings_list - - -def _warning(message: str) -> logging.Logger: - """Create a loader warning.""" - logging.getLogger("loader_warnings").warning(message) diff --git a/raillabel/exceptions.py b/raillabel/exceptions.py deleted file mode 100644 index 5be884b..0000000 --- a/raillabel/exceptions.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - - -class UnsupportedFormatError(Exception): - """Raised when a loaded annotation file is not in a supported format.""" - - __module__ = "raillabel" - - -class SchemaError(Exception): - """Raised when the data does not validate against a given schema.""" - - __module__ = "raillabel" - - -class AmbiguousSchemaNameError(Exception): - """Raised when a schema key applies to more than one schema files in. - - /schemas. - """ - - __module__ = "raillabel" - - -class MissingStreamError(Exception): - """Raised when a coordinate system has no corresponding stream.""" - - __module__ = "raillabel" - - -class MissingCoordinateSystemError(Exception): - """Raised when a stream has no corresponding coordinate system.""" - - __module__ = "raillabel" - - -class UnsupportedParentError(Exception): - """Raised when a coordinate system does not have 'base' as parent.""" - - __module__ = "raillabel" diff --git a/raillabel/filter/__init__.py b/raillabel/filter/__init__.py new file mode 100644 index 0000000..64231dd --- /dev/null +++ b/raillabel/filter/__init__.py @@ -0,0 +1,43 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 +"""Package for the raillabel filter functionality.""" + +from .end_time_filter import EndTimeFilter +from .exclude_annotation_id_filter import ExcludeAnnotationIdFilter +from .exclude_annotation_type_filter import ExcludeAnnotationTypeFilter +from .exclude_attributes_filter import ExcludeAttributesFilter +from .exclude_frame_id_filter import ExcludeFrameIdFilter +from .exclude_object_id_filter import ExcludeObjectIdFilter +from .exclude_object_type_filter import ExcludeObjectTypeFilter +from .exclude_sensor_id_filter import ExcludeSensorIdFilter +from .exclude_sensor_type_filter import ExcludeSensorTypeFilter +from .include_annotation_id_filter import IncludeAnnotationIdFilter +from .include_annotation_type_filter import IncludeAnnotationTypeFilter +from .include_attributes_filter import IncludeAttributesFilter +from .include_frame_id_filter import IncludeFrameIdFilter +from .include_object_id_filter import IncludeObjectIdFilter +from .include_object_type_filter import IncludeObjectTypeFilter +from .include_sensor_id_filter import IncludeSensorIdFilter +from .include_sensor_type_filter import IncludeSensorTypeFilter +from .start_time_filter import StartTimeFilter + +__all__ = [ + "IncludeFrameIdFilter", + "ExcludeFrameIdFilter", + "StartTimeFilter", + "EndTimeFilter", + "IncludeAnnotationIdFilter", + "ExcludeAnnotationIdFilter", + "IncludeAnnotationTypeFilter", + "ExcludeAnnotationTypeFilter", + "IncludeObjectIdFilter", + "ExcludeObjectIdFilter", + "IncludeObjectTypeFilter", + "ExcludeObjectTypeFilter", + "IncludeSensorIdFilter", + "ExcludeSensorIdFilter", + "IncludeSensorTypeFilter", + "ExcludeSensorTypeFilter", + "IncludeAttributesFilter", + "ExcludeAttributesFilter", +] diff --git a/raillabel/filter/_filter_abc.py b/raillabel/filter/_filter_abc.py new file mode 100644 index 0000000..3deb1b0 --- /dev/null +++ b/raillabel/filter/_filter_abc.py @@ -0,0 +1,34 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING +from uuid import UUID + +if TYPE_CHECKING: + from raillabel.format import Bbox, Cuboid, Frame, Poly2d, Poly3d, Scene, Seg3d + + +class _FilterAbc(ABC): + """Base class of all filter classes regardless of level.""" + + +class _AnnotationLevelFilter(_FilterAbc): + """Base class of all filter classes applied to the annotations.""" + + @abstractmethod + def passes_filter( + self, annotation_id: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, scene: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + raise NotImplementedError + + +class _FrameLevelFilter(_FilterAbc): + """Base class of all filter classes applied to the frames.""" + + @abstractmethod + def passes_filter(self, frame_id: int, frame: Frame) -> bool: + """Assess if a frame passes this filter.""" + raise NotImplementedError diff --git a/raillabel/filter/_filter_classes/__init__.py b/raillabel/filter/_filter_classes/__init__.py deleted file mode 100644 index 190e8a8..0000000 --- a/raillabel/filter/_filter_classes/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 -"""Package containing the loader classes for all supported formats.""" - -from importlib import import_module -from inspect import isclass -from pathlib import Path -from pkgutil import iter_modules - -# iterate through the modules in the current package -package_dir = str(Path(__file__).resolve().parent) -for _, module_name, _ in iter_modules([package_dir]): - - # import the module and iterate through its attributes - module = import_module(f"{__name__}.{module_name}") - for attribute_name in dir(module): - attribute = getattr(module, attribute_name) - - if isclass(attribute): - # Add the class to this package's variables - globals()[attribute_name] = attribute diff --git a/raillabel/filter/_filter_classes/_filter_abc.py b/raillabel/filter/_filter_classes/_filter_abc.py deleted file mode 100644 index 237e3a1..0000000 --- a/raillabel/filter/_filter_classes/_filter_abc.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from abc import ABC, abstractmethod, abstractproperty - -from ...format import Frame, _ObjectAnnotation - - -class _FilterABC(ABC): - """ABC for all filter classes. - - Creating a new filter - --------------------- - To create a new, custom filter create a new class in this dir, that inherits from _FilterABC. - Any class, that inherits from _FilterABC will automatically be loaded by the filter function. - Include the filter arguments (include_[...], exclude_[...], ...) in the PARAMETERS field. These - will be mutually exclusive.Select a level for the filter. The level determines where the filter - is going to be applied (e.g. at the frame level, annotation level, ...). Include the conditions - to pass the filter in the passes_filter() method, which returns True if the filter is passed. - The contents of the filter arguments can optionally be processed by the _process_filter_args(). - """ - - @property - @abstractproperty - def PARAMETERS(self) -> t.List[str]: - raise NotImplementedError - - @property - @abstractproperty - def LEVELS(self) -> t.List[str]: - raise NotImplementedError - - def __init__(self, kwargs): - - set_parameter = None - for param in self.PARAMETERS: - if param in kwargs and param is not None: - - if set_parameter is None: - setattr(self, param, self._process_filter_args(kwargs[param])) - set_parameter = param - else: - raise ValueError( - f"{set_parameter} and {param} are mutually exclusive, but were both set." - ) - - else: - setattr(self, param, None) - - @abstractmethod - def passes_filter(self, annotation: t.Union[t.Type[_ObjectAnnotation], Frame]) -> bool: - raise NotImplementedError - - def _process_filter_args(self, filter_args): - """Process filter arguments (optional).""" - return filter_args diff --git a/raillabel/filter/_filter_classes/_filter_annotation_ids.py b/raillabel/filter/_filter_classes/_filter_annotation_ids.py deleted file mode 100644 index 4ff50f5..0000000 --- a/raillabel/filter/_filter_classes/_filter_annotation_ids.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t - -from ._filter_abc import _FilterABC, _ObjectAnnotation - - -class _FilterAnnotationIds(_FilterABC): - - PARAMETERS = ["include_annotation_ids", "exclude_annotation_ids"] - LEVELS = ["annotation"] - - def passes_filter(self, annotation: t.Type[_ObjectAnnotation]) -> bool: - - if self.include_annotation_ids is not None: - return annotation.uid in self.include_annotation_ids - - elif self.exclude_annotation_ids is not None: - return annotation.uid not in self.exclude_annotation_ids - - else: - return True diff --git a/raillabel/filter/_filter_classes/_filter_annotation_types.py b/raillabel/filter/_filter_classes/_filter_annotation_types.py deleted file mode 100644 index a929983..0000000 --- a/raillabel/filter/_filter_classes/_filter_annotation_types.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t - -from ._filter_abc import _FilterABC, _ObjectAnnotation - - -class _FilterAnnotationTypes(_FilterABC): - - PARAMETERS = ["include_annotation_types", "exclude_annotation_types"] - LEVELS = ["annotation"] - - def passes_filter(self, annotation: t.Type[_ObjectAnnotation]) -> bool: - - if self.include_annotation_types is not None: - return annotation.__class__.__name__.lower() in self.include_annotation_types - - elif self.exclude_annotation_types is not None: - return annotation.__class__.__name__.lower() not in self.exclude_annotation_types - - else: - return True - - def _process_filter_args(self, filter_args): - return [arg.lower() for arg in filter_args] diff --git a/raillabel/filter/_filter_classes/_filter_attributes.py b/raillabel/filter/_filter_classes/_filter_attributes.py deleted file mode 100644 index 01d3450..0000000 --- a/raillabel/filter/_filter_classes/_filter_attributes.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t - -from ._filter_abc import _FilterABC, _ObjectAnnotation - - -class _FilterAttributes(_FilterABC): - - PARAMETERS = ["include_attributes", "exclude_attributes"] - LEVELS = ["annotation"] - - def passes_filter(self, annotation: t.Type[_ObjectAnnotation]) -> bool: - - if self.include_attributes is not None: - for attribute_id, attribute_val in self.include_attributes.items(): - - if attribute_val is None: - if attribute_id not in annotation.attributes: - return False - - else: - if ( - attribute_id not in annotation.attributes - or annotation.attributes[attribute_id] != attribute_val - ): - return False - - elif self.exclude_attributes is not None: - - for attribute_id, attribute_val in self.exclude_attributes.items(): - - if attribute_val is None: - if attribute_id in annotation.attributes: - return False - - else: - if ( - attribute_id in annotation.attributes - and attribute_val == annotation.attributes[attribute_id] - ): - return False - - return True diff --git a/raillabel/filter/_filter_classes/_filter_end.py b/raillabel/filter/_filter_classes/_filter_end.py deleted file mode 100644 index 5673447..0000000 --- a/raillabel/filter/_filter_classes/_filter_end.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from decimal import Decimal - -from ._filter_abc import Frame, _FilterABC - - -class _FilterEnd(_FilterABC): - - PARAMETERS = ["end_frame", "end_timestamp"] - LEVELS = ["frame"] - - def passes_filter(self, frame: Frame) -> bool: - - if self.end_frame is not None: - return frame.uid <= self.end_frame - - elif self.end_timestamp is not None: - return frame.timestamp <= Decimal(self.end_timestamp) - - else: - return True diff --git a/raillabel/filter/_filter_classes/_filter_frames.py b/raillabel/filter/_filter_classes/_filter_frames.py deleted file mode 100644 index 6cfd7fd..0000000 --- a/raillabel/filter/_filter_classes/_filter_frames.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from ._filter_abc import Frame, _FilterABC - - -class _FilterFrame(_FilterABC): - - PARAMETERS = ["include_frames", "exclude_frames"] - LEVELS = ["frame"] - - def passes_filter(self, frame: Frame) -> bool: - - if self.include_frames is not None: - return int(frame.uid) in self.include_frames - - elif self.exclude_frames is not None: - return int(frame.uid) not in self.exclude_frames - - else: - return True diff --git a/raillabel/filter/_filter_classes/_filter_object_ids.py b/raillabel/filter/_filter_classes/_filter_object_ids.py deleted file mode 100644 index aa293fa..0000000 --- a/raillabel/filter/_filter_classes/_filter_object_ids.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t - -from ._filter_abc import _FilterABC, _ObjectAnnotation - - -class _FilterObjectIds(_FilterABC): - - PARAMETERS = ["include_object_ids", "exclude_object_ids"] - LEVELS = ["annotation"] - - def passes_filter(self, annotation: t.Type[_ObjectAnnotation]) -> bool: - - if self.include_object_ids is not None: - return annotation.object.uid in self.include_object_ids - - elif self.exclude_object_ids is not None: - return annotation.object.uid not in self.exclude_object_ids - - else: - return True diff --git a/raillabel/filter/_filter_classes/_filter_object_types.py b/raillabel/filter/_filter_classes/_filter_object_types.py deleted file mode 100644 index 2c652b3..0000000 --- a/raillabel/filter/_filter_classes/_filter_object_types.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t - -from ._filter_abc import _FilterABC, _ObjectAnnotation - - -class _FilterObjectTypes(_FilterABC): - - PARAMETERS = ["include_object_types", "exclude_object_types"] - LEVELS = ["annotation"] - - def passes_filter(self, annotation: t.Type[_ObjectAnnotation]) -> bool: - - if self.include_object_types is not None: - return annotation.object.type in self.include_object_types - - elif self.exclude_object_types is not None: - return annotation.object.type not in self.exclude_object_types - - else: - return True diff --git a/raillabel/filter/_filter_classes/_filter_sensors.py b/raillabel/filter/_filter_classes/_filter_sensors.py deleted file mode 100644 index 2bdf2b4..0000000 --- a/raillabel/filter/_filter_classes/_filter_sensors.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t - -from ._filter_abc import _FilterABC, _ObjectAnnotation - - -class _FilterSensors(_FilterABC): - - PARAMETERS = ["include_sensors", "exclude_sensors"] - LEVELS = ["frame_data", "annotation"] - - def passes_filter(self, annotation: t.Type[_ObjectAnnotation]) -> bool: - - if self.include_sensors is not None: - return annotation.sensor.uid in self.include_sensors - - elif self.exclude_sensors is not None: - return annotation.sensor.uid not in self.exclude_sensors - - else: - return True diff --git a/raillabel/filter/_filter_classes/_filter_start.py b/raillabel/filter/_filter_classes/_filter_start.py deleted file mode 100644 index ab1e00b..0000000 --- a/raillabel/filter/_filter_classes/_filter_start.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from decimal import Decimal - -from ._filter_abc import Frame, _FilterABC - - -class _FilterStart(_FilterABC): - - PARAMETERS = ["start_frame", "start_timestamp"] - LEVELS = ["frame"] - - def passes_filter(self, frame: Frame) -> bool: - - if self.start_frame is not None: - return frame.uid >= self.start_frame - - elif self.start_timestamp is not None: - return frame.timestamp >= Decimal(self.start_timestamp) - - else: - return True diff --git a/raillabel/filter/end_time_filter.py b/raillabel/filter/end_time_filter.py new file mode 100644 index 0000000..2b59fc7 --- /dev/null +++ b/raillabel/filter/end_time_filter.py @@ -0,0 +1,25 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from decimal import Decimal + +from raillabel.format import Frame + +from ._filter_abc import _FrameLevelFilter + + +@dataclass +class EndTimeFilter(_FrameLevelFilter): + """Filter out all the frames in the scene with timestamps higher than the end_time.""" + + end_time: float | Decimal + + def passes_filter(self, _: int, frame: Frame) -> bool: + """Assess if a frame passes this filter.""" + if frame.timestamp is not None: + return frame.timestamp <= self.end_time + + return True diff --git a/raillabel/filter/exclude_annotation_id_filter.py b/raillabel/filter/exclude_annotation_id_filter.py new file mode 100644 index 0000000..a41830f --- /dev/null +++ b/raillabel/filter/exclude_annotation_id_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class ExcludeAnnotationIdFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do have disallowed ids.""" + + annotation_ids: set[UUID] | list[UUID] + + def passes_filter( + self, annotation_id: UUID, _: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return annotation_id not in self.annotation_ids diff --git a/raillabel/filter/exclude_annotation_type_filter.py b/raillabel/filter/exclude_annotation_type_filter.py new file mode 100644 index 0000000..80180dd --- /dev/null +++ b/raillabel/filter/exclude_annotation_type_filter.py @@ -0,0 +1,48 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class ExcludeAnnotationTypeFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do have the type (like bbox or cuboid).""" + + annotation_types: ( + set[Literal["bbox", "cuboid", "poly2d", "poly3d", "seg3d"]] + | list[Literal["bbox", "cuboid", "poly2d", "poly3d", "seg3d"]] + ) + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + annotation_type_str = None + + if isinstance(annotation, Bbox): + annotation_type_str = "bbox" + + elif isinstance(annotation, Cuboid): + annotation_type_str = "cuboid" + + elif isinstance(annotation, Poly2d): + annotation_type_str = "poly2d" + + elif isinstance(annotation, Poly3d): + annotation_type_str = "poly3d" + + elif isinstance(annotation, Seg3d): + annotation_type_str = "seg3d" + + else: + raise TypeError + + return annotation_type_str not in self.annotation_types diff --git a/raillabel/filter/exclude_attributes_filter.py b/raillabel/filter/exclude_attributes_filter.py new file mode 100644 index 0000000..b2b33e8 --- /dev/null +++ b/raillabel/filter/exclude_attributes_filter.py @@ -0,0 +1,40 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class ExcludeAttributesFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do have matching attributes. + + If an attribute has None as the value, all annotations are included, that do not have this + attribute. If the value is anything other than None, all annotations are included that do not + have the attribute or where the value does not match. + """ + + attributes: dict[str, bool | float | str | list | None] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + for attribute_name, attribute_value in self.attributes.items(): + if attribute_value is None: + if attribute_name in annotation.attributes: + return False + + elif ( + attribute_name in annotation.attributes + and attribute_value == annotation.attributes[attribute_name] + ): + return False + + return True diff --git a/raillabel/filter/exclude_frame_id_filter.py b/raillabel/filter/exclude_frame_id_filter.py new file mode 100644 index 0000000..d89a367 --- /dev/null +++ b/raillabel/filter/exclude_frame_id_filter.py @@ -0,0 +1,21 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.format import Frame + +from ._filter_abc import _FrameLevelFilter + + +@dataclass +class ExcludeFrameIdFilter(_FrameLevelFilter): + """Filter out all the frames in the scene that do match a list of disallowed ids.""" + + frame_ids: set[int] | list[int] + + def passes_filter(self, frame_id: int, _: Frame) -> bool: + """Assess if a frame passes this filter.""" + return frame_id not in self.frame_ids diff --git a/raillabel/filter/exclude_object_id_filter.py b/raillabel/filter/exclude_object_id_filter.py new file mode 100644 index 0000000..f840d8e --- /dev/null +++ b/raillabel/filter/exclude_object_id_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class ExcludeObjectIdFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do have matching object ids.""" + + object_ids: list[UUID] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return annotation.object_id not in self.object_ids diff --git a/raillabel/filter/exclude_object_type_filter.py b/raillabel/filter/exclude_object_type_filter.py new file mode 100644 index 0000000..bdd26d9 --- /dev/null +++ b/raillabel/filter/exclude_object_type_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class ExcludeObjectTypeFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do match the type (like 'person').""" + + object_types: list[str] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, scene: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return scene.objects[annotation.object_id].type not in self.object_types diff --git a/raillabel/filter/exclude_sensor_id_filter.py b/raillabel/filter/exclude_sensor_id_filter.py new file mode 100644 index 0000000..f2375cf --- /dev/null +++ b/raillabel/filter/exclude_sensor_id_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class ExcludeSensorIdFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT have matching sensor ids.""" + + sensor_ids: set[str] | list[str] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return annotation.sensor_id not in self.sensor_ids diff --git a/raillabel/filter/exclude_sensor_type_filter.py b/raillabel/filter/exclude_sensor_type_filter.py new file mode 100644 index 0000000..a4d9d07 --- /dev/null +++ b/raillabel/filter/exclude_sensor_type_filter.py @@ -0,0 +1,25 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class ExcludeSensorTypeFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do match the sensor type (like 'camera').""" + + sensor_types: list[Literal["camera", "lidar", "radar", "gps_imu", "other"]] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, scene: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return scene.sensors[annotation.sensor_id].TYPE not in self.sensor_types diff --git a/raillabel/filter/filter.py b/raillabel/filter/filter.py deleted file mode 100644 index e3ee76e..0000000 --- a/raillabel/filter/filter.py +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import pickle -import typing as t -from warnings import warn - -from typing_extensions import deprecated - -from .. import format -from . import _filter_classes - - -@deprecated( - "filter() will not be included in any future releases. Use `scene.filter()` in those versions instead" -) -def filter(scene: format.Scene, **kwargs) -> format.Scene: - """Return a copy of the scene with the annotations filtered. - - Parameters - ---------- - scene: raillabel.Scene - Scene, which should be copied and filtered. - include_object_types: str or list of str, optional - List of class/type names that should be included in the filtered scene. If set, no - other classes/types will be copied. Mutually exclusive with exclude_object_types. - exclude_object_types: str or list of str, optional - List of class/type names that should be excluded in the filtered scene. If set, all - other classes/types will be copied. Mutually exclusive with include_object_types. - include_annotation_types: str or list of str, optional - List of annotation types (i.e. bboxs, cuboids, poly2ds, seg3ds) that should be included - in the filtered scene. If set, no other annotation types will be copied. Mutually - exclusive with exclude_annotation_types. - exclude_annotation_types: str or list of str, optional - List of annotation types (i.e. bboxs, cuboids, poly2ds, seg3ds) that should be excluded - in the filtered scene. If set, all other annotation types will be copied. Mutually - exclusive with include_annotation_types. - include_annotation_ids: str or list of str, optional - List of annotation UIDs that should be included in the filtered scene. If set, no other - annotation UIDs will be copied. Mutually exclusive with exclude_annotation_ids. - exclude_annotation_ids: str or list of str, optional - List of annotation UIDs that should be excluded in the filtered scene. If set, all - other annotation UIDs will be copied. Mutually exclusive with include_annotation_ids. - include_object_ids: str or list of str, optional - List of object UIDs that should be included in the filtered scene. If set, no other - objects will be copied. Mutually exclusive with exclude_object_ids. - exclude_object_ids: str or list of str, optional - List of object UIDs that should be excluded in the filtered scene. If set, all other - objects will be copied. Mutually exclusive with include_object_ids. - include_sensors: str or list of str - List of sensors that should be included in the filtered scene. If set, no other - sensors will be copied. Mutually exclusive with exclude_sensors. - exclude_sensors: str or list of str, optional - List of sensors that should be excluded in the filtered scene. If set, all other - sensors will be copied. Mutually exclusive with include_sensors. - include_attributes: dict, optional - Dict of attributes that should be included in the filtered scene. Dict keys are the - attribute names, values are the specific values that should be included. If the - value is set so None, all annotations with the attribute are included regardless of - value. Mutually exclusive with exclude_attributes. - exclude_attributes: dict, optional - Dict of attributes that should be excluded in the filtered scene. Dict keys are the - attribute names, values are the specific values that should be excluded. If the value - is set so None, all annotations with the attribute are excluded regardless of value. - Mutually exclusive with include_attributes. - include_frames: int or list of int, optional - List of frame UIDs that should be included in the filtered scene. If set, no other - frames will be copied. Mutually exclusive with exclude_frames. - exclude_frames: int or list of int, optional - List of frame UIDs that should be excluded in the filtered scene. If set, all other - frames will be copied. Mutually exclusive with include_frames. - start_frame: int, optional - Frame at which the filtered scene should start. Mutually exclusive with s - tart_timestamp. - end_frame: int, optional - Frame at which the filtered scene should end (inclusive). Mutually exclusive with - end_timestamp. - start_timestamp: decimal.Decimal, optional - Unix timestamp at which the filtered scene should start (inclusive). Mutually exclusive - with start_frame. - end_timestamp: decimal.Decimal, optional - Unix timestamp at which the filtered scene should end (inclusive). Mutually exclusive - with end_frame. - - Raises - ------ - ValueError - if two mutually exclusive parameters are set. - TypeError - if an unexpected keyword argument has been set. - """ - warn( - "filter() will not be included in any future releases. Use `scene.filter()` in those versions instead" - ) - - filters_by_level = _collect_filter_classes(kwargs) - filtered_scene, used_sensors, used_objects = _filter_scene(_copy(scene), filters_by_level) - filtered_scene = _remove_unused(filtered_scene, used_sensors, used_objects) - - return filtered_scene - - -# --- Prepare filter classes - - -def _collect_filter_classes(kwargs) -> t.Tuple[t.List[t.Type], t.List[str]]: - filters = [] - supported_kwargs = [] - for cls in _filter_classes.__dict__.values(): - if ( - isinstance(cls, type) - and issubclass(cls, _filter_classes._FilterABC) - and cls != _filter_classes._FilterABC - ): - filters.append(cls(kwargs)) - supported_kwargs.extend(cls.PARAMETERS) - - _check_for_unsupported_arg(kwargs, supported_kwargs) - - return _seperate_filters_by_level(filters) - - -def _check_for_unsupported_arg(kwargs: t.List[str], supported_kwargs: t.List[str]): - for arg in kwargs: - if arg not in supported_kwargs: - raise TypeError( - f"filter() got an unexpected keyword argument '{arg}'. Supported keyword " - + f"arguments: {sorted(supported_kwargs)}" - ) - - -def _seperate_filters_by_level(filters: t.List[t.Type]) -> t.Dict[str, t.List[t.Type]]: - all_filter_levels = [level for f in filters for level in f.LEVELS] - - filters_by_level = {level: [] for level in all_filter_levels} - for level in filters_by_level: - for filter_class in filters: - if level in filter_class.LEVELS: - filters_by_level[level].append(filter_class) - - return filters_by_level - - -# --- Filter scene - - -def _filter_scene( - scene: format.Scene, filters_by_level: t.Dict[str, t.List[t.Type]] -) -> t.Tuple[format.Scene, t.Set[str], t.Set[str]]: - - used_sensors = set() - used_objects = set() - - for frame_id, frame in list(scene.frames.items()): - - if not _passes_filters(frame, filters_by_level["frame"]): - del scene.frames[frame_id] - continue - - for frame_data_id, frame_data in list(frame.frame_data.items()): - - if _passes_filters(frame_data, filters_by_level["frame_data"]): - used_sensors.add(frame_data.sensor.uid) - - else: - del scene.frames[frame_id].frame_data[frame_data_id] - - for annotation_id, annotation in list(frame.annotations.items()): - - if _passes_filters(annotation, filters_by_level["annotation"]): - used_objects.add(annotation.object.uid) - used_sensors.add(annotation.sensor.uid) - - else: - del scene.frames[frame_id].annotations[annotation_id] - - return scene, used_sensors, used_objects - - -# --- Remove unused - - -def _remove_unused( - scene: format.Scene, used_sensors: t.Set[str], used_objects: t.Set[str] -) -> format.Scene: - - scene = _remove_unused_sensors(scene, used_sensors) - scene = _remove_unused_objects(scene, used_objects) - - for frame_id in scene.frames: - - scene.frames[frame_id] = _remove_unused_sensor_references( - scene.frames[frame_id], used_sensors - ) - - return scene - - -def _remove_unused_sensors(scene: format.Scene, used_sensors: t.Set[str]) -> format.Scene: - for sensor_id in list(scene.sensors): - if sensor_id not in used_sensors: - del scene.sensors[sensor_id] - - return scene - - -def _remove_unused_objects(scene: format.Scene, used_objects: t.Set[str]) -> format.Scene: - for object_id in list(scene.objects): - if object_id not in used_objects: - del scene.objects[object_id] - - return scene - - -def _remove_unused_sensor_references(frame: format.Frame, used_sensors: t.Set[str]) -> format.Frame: - for sensor_id in list(frame.sensors): - if sensor_id not in used_sensors: - del frame.sensors[sensor_id] - - return frame - - -# --- Helper functions - - -def _passes_filters(data, filters): - for f in filters: - if not f.passes_filter(data): - return False - - return True - - -def _copy(object): - return pickle.loads(pickle.dumps(object, -1)) diff --git a/raillabel/filter/include_annotation_id_filter.py b/raillabel/filter/include_annotation_id_filter.py new file mode 100644 index 0000000..7cac89b --- /dev/null +++ b/raillabel/filter/include_annotation_id_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class IncludeAnnotationIdFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT have the correct ids.""" + + annotation_ids: set[UUID] | list[UUID] + + def passes_filter( + self, annotation_id: UUID, _: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return annotation_id in self.annotation_ids diff --git a/raillabel/filter/include_annotation_type_filter.py b/raillabel/filter/include_annotation_type_filter.py new file mode 100644 index 0000000..300be80 --- /dev/null +++ b/raillabel/filter/include_annotation_type_filter.py @@ -0,0 +1,48 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class IncludeAnnotationTypeFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT have the type (like bbox or cuboid).""" + + annotation_types: ( + set[Literal["bbox", "cuboid", "poly2d", "poly3d", "seg3d"]] + | list[Literal["bbox", "cuboid", "poly2d", "poly3d", "seg3d"]] + ) + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + annotation_type_str = None + + if isinstance(annotation, Bbox): + annotation_type_str = "bbox" + + elif isinstance(annotation, Cuboid): + annotation_type_str = "cuboid" + + elif isinstance(annotation, Poly2d): + annotation_type_str = "poly2d" + + elif isinstance(annotation, Poly3d): + annotation_type_str = "poly3d" + + elif isinstance(annotation, Seg3d): + annotation_type_str = "seg3d" + + else: + raise TypeError + + return annotation_type_str in self.annotation_types diff --git a/raillabel/filter/include_attributes_filter.py b/raillabel/filter/include_attributes_filter.py new file mode 100644 index 0000000..7071c17 --- /dev/null +++ b/raillabel/filter/include_attributes_filter.py @@ -0,0 +1,39 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class IncludeAttributesFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT have matching attributes. + + If an attribute has None as the value, all annotations are excluded, that do not have this + attribute. If the value is anything other than None, all annotations are excluded that do not + have the attribute or where the value does not match. + """ + + attributes: dict[str, bool | float | str | list | None] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + for attribute_name, attribute_value in annotation.attributes.items(): + if attribute_name not in self.attributes: + return False + + if ( + self.attributes[attribute_name] is not None + and attribute_value != self.attributes[attribute_name] + ): + return False + + return True diff --git a/raillabel/filter/include_frame_id_filter.py b/raillabel/filter/include_frame_id_filter.py new file mode 100644 index 0000000..335eb7b --- /dev/null +++ b/raillabel/filter/include_frame_id_filter.py @@ -0,0 +1,21 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.format import Frame + +from ._filter_abc import _FrameLevelFilter + + +@dataclass +class IncludeFrameIdFilter(_FrameLevelFilter): + """Filter out all the frames in the scene that do NOT match a list of allowed ids.""" + + frame_ids: set[int] | list[int] + + def passes_filter(self, frame_id: int, _: Frame) -> bool: + """Assess if a frame passes this filter.""" + return frame_id in self.frame_ids diff --git a/raillabel/filter/include_object_id_filter.py b/raillabel/filter/include_object_id_filter.py new file mode 100644 index 0000000..e4bccb9 --- /dev/null +++ b/raillabel/filter/include_object_id_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class IncludeObjectIdFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT have matching object ids.""" + + object_ids: list[UUID] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return annotation.object_id in self.object_ids diff --git a/raillabel/filter/include_object_type_filter.py b/raillabel/filter/include_object_type_filter.py new file mode 100644 index 0000000..aa1a352 --- /dev/null +++ b/raillabel/filter/include_object_type_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class IncludeObjectTypeFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT match the type (like 'person').""" + + object_types: list[str] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, scene: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return scene.objects[annotation.object_id].type in self.object_types diff --git a/raillabel/filter/include_sensor_id_filter.py b/raillabel/filter/include_sensor_id_filter.py new file mode 100644 index 0000000..49ce83b --- /dev/null +++ b/raillabel/filter/include_sensor_id_filter.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class IncludeSensorIdFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT have matching sensor ids.""" + + sensor_ids: set[str] | list[str] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, __: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return annotation.sensor_id in self.sensor_ids diff --git a/raillabel/filter/include_sensor_type_filter.py b/raillabel/filter/include_sensor_type_filter.py new file mode 100644 index 0000000..527f752 --- /dev/null +++ b/raillabel/filter/include_sensor_type_filter.py @@ -0,0 +1,25 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal +from uuid import UUID + +from raillabel.format import Bbox, Cuboid, Poly2d, Poly3d, Scene, Seg3d + +from ._filter_abc import _AnnotationLevelFilter + + +@dataclass +class IncludeSensorTypeFilter(_AnnotationLevelFilter): + """Filter out all annotations in the scene, that do NOT match the sensor type (like 'camera').""" + + sensor_types: list[Literal["camera", "lidar", "radar", "gps_imu", "other"]] + + def passes_filter( + self, _: UUID, annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, scene: Scene + ) -> bool: + """Assess if an annotation passes this filter.""" + return scene.sensors[annotation.sensor_id].TYPE in self.sensor_types diff --git a/raillabel/filter/start_time_filter.py b/raillabel/filter/start_time_filter.py new file mode 100644 index 0000000..8ce8c7e --- /dev/null +++ b/raillabel/filter/start_time_filter.py @@ -0,0 +1,25 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from decimal import Decimal + +from raillabel.format import Frame + +from ._filter_abc import _FrameLevelFilter + + +@dataclass +class StartTimeFilter(_FrameLevelFilter): + """Filter out all the frames in the scene with timestamps lower than the start_time.""" + + start_time: float | Decimal + + def passes_filter(self, _: int, frame: Frame) -> bool: + """Assess if a frame passes this filter.""" + if frame.timestamp is not None: + return frame.timestamp >= self.start_time + + return True diff --git a/raillabel/format/__init__.py b/raillabel/format/__init__.py index 5af563e..caab9fc 100644 --- a/raillabel/format/__init__.py +++ b/raillabel/format/__init__.py @@ -2,26 +2,57 @@ # SPDX-License-Identifier: Apache-2.0 """Module containing all relevant format classes.""" -from .raillabel._object_annotation import _ObjectAnnotation, annotation_classes -from .raillabel.bbox import Bbox -from .raillabel.cuboid import Cuboid -from .raillabel.element_data_pointer import ElementDataPointer -from .raillabel.frame import Frame -from .raillabel.frame_interval import FrameInterval -from .raillabel.intrinsics_pinhole import IntrinsicsPinhole -from .raillabel.intrinsics_radar import IntrinsicsRadar -from .raillabel.metadata import Metadata -from .raillabel.num import Num -from .raillabel.object import Object -from .raillabel.point2d import Point2d -from .raillabel.point3d import Point3d -from .raillabel.poly2d import Poly2d -from .raillabel.poly3d import Poly3d -from .raillabel.quaternion import Quaternion -from .raillabel.scene import Scene -from .raillabel.seg3d import Seg3d -from .raillabel.sensor import Sensor, SensorType -from .raillabel.sensor_reference import SensorReference -from .raillabel.size2d import Size2d -from .raillabel.size3d import Size3d -from .raillabel.transform import Transform +from .bbox import Bbox +from .camera import Camera +from .cuboid import Cuboid +from .frame import Frame +from .frame_interval import FrameInterval +from .gps_imu import GpsImu +from .intrinsics_pinhole import IntrinsicsPinhole +from .intrinsics_radar import IntrinsicsRadar +from .lidar import Lidar +from .metadata import Metadata +from .num import Num +from .object import Object +from .other_sensor import OtherSensor +from .point2d import Point2d +from .point3d import Point3d +from .poly2d import Poly2d +from .poly3d import Poly3d +from .quaternion import Quaternion +from .radar import Radar +from .scene import Scene +from .seg3d import Seg3d +from .sensor_reference import SensorReference +from .size2d import Size2d +from .size3d import Size3d +from .transform import Transform + +__all__ = [ + "Bbox", + "Camera", + "Cuboid", + "ElementDataPointer", + "Frame", + "FrameInterval", + "GpsImu", + "IntrinsicsPinhole", + "IntrinsicsRadar", + "Lidar", + "Metadata", + "Num", + "Object", + "OtherSensor", + "Point2d", + "Point3d", + "Poly2d", + "Poly3d", + "Quaternion", + "Radar", + "Scene", + "Seg3d", + "SensorReference", + "Size2d", + "Size3d", + "Transform", +] diff --git a/raillabel/format/_attributes.py b/raillabel/format/_attributes.py new file mode 100644 index 0000000..a0bd42c --- /dev/null +++ b/raillabel/format/_attributes.py @@ -0,0 +1,76 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from raillabel.json_format import ( + JSONAttributes, + JSONBooleanAttribute, + JSONNumAttribute, + JSONTextAttribute, + JSONVecAttribute, +) + + +def _attributes_from_json(json: JSONAttributes | None) -> dict[str, float | bool | str | list]: + """Parse the annotation attributes from json.""" + if json is None: + return {} + + attributes: dict[str, float | bool | str | list] = {} + + if json.boolean is not None: + for bool_attribute in json.boolean: + attributes[bool_attribute.name] = bool_attribute.val + + if json.num is not None: + for num_attribute in json.num: + attributes[num_attribute.name] = num_attribute.val + + if json.text is not None: + for text_attribute in json.text: + attributes[text_attribute.name] = text_attribute.val + + if json.vec is not None: + for vec_attribute in json.vec: + attributes[vec_attribute.name] = vec_attribute.val + + return attributes + + +def _attributes_to_json(attributes: dict[str, float | bool | str | list]) -> JSONAttributes | None: + if len(attributes) == 0: + return None + + boolean_attributes = [] + num_attributes = [] + text_attributes = [] + vec_attributes = [] + + for name, value in attributes.items(): + if isinstance(value, bool): + boolean_attributes.append(JSONBooleanAttribute(name=name, val=value)) + + elif isinstance(value, (int, float)): + num_attributes.append(JSONNumAttribute(name=name, val=value)) + + elif isinstance(value, str): + text_attributes.append(JSONTextAttribute(name=name, val=value)) + + elif isinstance(value, list): + vec_attributes.append(JSONVecAttribute(name=name, val=value)) + + else: + raise UnsupportedAttributeTypeError(name, value) + + return JSONAttributes( + boolean=boolean_attributes, num=num_attributes, text=text_attributes, vec=vec_attributes + ) + + +class UnsupportedAttributeTypeError(TypeError): + def __init__(self, attribute_name: str, attribute_value: object) -> None: + super().__init__( + f"{attribute_value.__class__.__name__} of attribute {attribute_name} " + "is not a supported attribute type" + ) diff --git a/raillabel/format/_sensor_without_intrinsics.py b/raillabel/format/_sensor_without_intrinsics.py new file mode 100644 index 0000000..24522ff --- /dev/null +++ b/raillabel/format/_sensor_without_intrinsics.py @@ -0,0 +1,30 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import JSONTransformData + +from .transform import Transform + + +@dataclass +class _SensorWithoutIntrinsics: + """Parent class of all sensors, that do not have an intrinsic calibration.""" + + extrinsics: Transform | None = None + "External calibration of the sensor defined by the 3D transform to the coordinate system origin." + + uri: str | None = None + "Name of the subdirectory containing the sensor files." + + description: str | None = None + "Additional information about the sensor." + + +def _extrinsics_from_json(json_transform: JSONTransformData | None) -> Transform | None: + if json_transform is None: + return None + return Transform.from_json(json_transform) diff --git a/raillabel/format/_util.py b/raillabel/format/_util.py new file mode 100644 index 0000000..02f5e57 --- /dev/null +++ b/raillabel/format/_util.py @@ -0,0 +1,16 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + + +def _empty_list_to_none(collection: list | None) -> list | None: + if collection is None: + return None + if len(collection) == 0: + return None + return collection + + +def _flatten_list(list_of_tuples: list[tuple]) -> list: + return [item for tup in list_of_tuples for item in tup] diff --git a/raillabel/format/bbox.py b/raillabel/format/bbox.py new file mode 100644 index 0000000..cd58b41 --- /dev/null +++ b/raillabel/format/bbox.py @@ -0,0 +1,58 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.json_format import JSONBbox + +from ._attributes import _attributes_from_json, _attributes_to_json +from .point2d import Point2d +from .size2d import Size2d + + +@dataclass +class Bbox: + """A 2D bounding box in an image.""" + + pos: Point2d + "The center point of the bbox in pixels." + + size: Size2d + "The dimensions of the bbox in pixels from the top left corner to the bottom right corner." + + object_id: UUID + "The unique identifyer of the real-life object, this annotation belongs to." + + sensor_id: str + "The unique identifyer of the sensor this annotation is labeled in." + + attributes: dict[str, float | bool | str | list] + "Additional information associated with the annotation." + + @classmethod + def from_json(cls, json: JSONBbox, object_id: UUID) -> Bbox: + """Construct an instant of this class from RailLabel JSON data.""" + return Bbox( + pos=Point2d.from_json((json.val[0], json.val[1])), + size=Size2d.from_json((json.val[2], json.val[3])), + object_id=object_id, + sensor_id=json.coordinate_system, + attributes=_attributes_from_json(json.attributes), + ) + + def to_json(self, uid: UUID, object_type: str) -> JSONBbox: + """Export this object into the RailLabel JSON format.""" + return JSONBbox( + name=self.name(object_type), + val=list(self.pos.to_json()) + list(self.size.to_json()), + coordinate_system=self.sensor_id, + uid=uid, + attributes=_attributes_to_json(self.attributes), + ) + + def name(self, object_type: str) -> str: + """Return the name of the annotation used for indexing in the object data pointers.""" + return f"{self.sensor_id}__bbox__{object_type}" diff --git a/raillabel/format/camera.py b/raillabel/format/camera.py new file mode 100644 index 0000000..263ee91 --- /dev/null +++ b/raillabel/format/camera.py @@ -0,0 +1,71 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import ( + JSONCoordinateSystem, + JSONStreamCamera, + JSONStreamCameraProperties, + JSONTransformData, +) + +from .intrinsics_pinhole import IntrinsicsPinhole +from .transform import Transform + + +@dataclass +class Camera: + """A camera sensor.""" + + intrinsics: IntrinsicsPinhole + "The intrinsic calibration of the sensor." + + extrinsics: Transform | None = None + "External calibration of the sensor defined by the 3D transform to the coordinate system origin." + + uri: str | None = None + "Name of the subdirectory containing the sensor files." + + description: str | None = None + "Additional information about the sensor." + + TYPE: str = "camera" + + @classmethod + def from_json( + cls, json_stream: JSONStreamCamera, json_coordinate_system: JSONCoordinateSystem + ) -> Camera: + """Construct an instant of this class from RailLabel JSON data.""" + return Camera( + intrinsics=IntrinsicsPinhole.from_json(json_stream.stream_properties.intrinsics_pinhole), + extrinsics=_extrinsics_from_json(json_coordinate_system.pose_wrt_parent), + uri=json_stream.uri, + description=json_stream.description, + ) + + def to_json(self) -> tuple[JSONStreamCamera, JSONCoordinateSystem]: + """Export this object into the RailLabel JSON format.""" + return ( + JSONStreamCamera( + type="camera", + stream_properties=JSONStreamCameraProperties( + intrinsics_pinhole=self.intrinsics.to_json() + ), + uri=self.uri, + description=self.description, + ), + JSONCoordinateSystem( + parent="base", + type="sensor", + pose_wrt_parent=self.extrinsics.to_json() if self.extrinsics is not None else None, + ), + ) + + +def _extrinsics_from_json(json_transform: JSONTransformData | None) -> Transform | None: + if json_transform is None: + return None + return Transform.from_json(json_transform) diff --git a/raillabel/format/cuboid.py b/raillabel/format/cuboid.py new file mode 100644 index 0000000..227b9f9 --- /dev/null +++ b/raillabel/format/cuboid.py @@ -0,0 +1,64 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.json_format import JSONCuboid + +from ._attributes import _attributes_from_json, _attributes_to_json +from .point3d import Point3d +from .quaternion import Quaternion +from .size3d import Size3d + + +@dataclass +class Cuboid: + """3D bounding box.""" + + pos: Point3d + """The center position of the cuboid in meters, where the x coordinate points ahead of the + vehicle, y points to the left and z points upwards.""" + + quat: Quaternion + "The rotation of the cuboid in quaternions." + + size: Size3d + "The size of the cuboid in meters." + + object_id: UUID + "The unique identifyer of the real-life object, this annotation belongs to." + + sensor_id: str + "The unique identifyer of the sensor this annotation is labeled in." + + attributes: dict[str, float | bool | str | list] + "Additional information associated with the annotation." + + @classmethod + def from_json(cls, json: JSONCuboid, object_id: UUID) -> Cuboid: + """Construct an instant of this class from RailLabel JSON data.""" + return Cuboid( + pos=Point3d.from_json((json.val[0], json.val[1], json.val[2])), + quat=Quaternion.from_json((json.val[3], json.val[4], json.val[5], json.val[6])), + size=Size3d.from_json((json.val[7], json.val[8], json.val[9])), + object_id=object_id, + sensor_id=json.coordinate_system, + attributes=_attributes_from_json(json.attributes), + ) + + def to_json(self, uid: UUID, object_type: str) -> JSONCuboid: + """Export this object into the RailLabel JSON format.""" + return JSONCuboid( + name=self.name(object_type), + val=list(self.pos.to_json()) + list(self.quat.to_json()) + list(self.size.to_json()), + coordinate_system=self.sensor_id, + uid=uid, + attributes=_attributes_to_json(self.attributes), + ) + + def name(self, object_type: str) -> str: + """Return the name of the annotation used for indexing in the object data pointers.""" + return f"{self.sensor_id}__cuboid__{object_type}" diff --git a/raillabel/format/frame.py b/raillabel/format/frame.py new file mode 100644 index 0000000..91fb7c3 --- /dev/null +++ b/raillabel/format/frame.py @@ -0,0 +1,198 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass, field +from decimal import Decimal +from uuid import UUID + +from raillabel.json_format import ( + JSONAnnotations, + JSONFrame, + JSONFrameData, + JSONFrameProperties, + JSONObjectData, +) + +from ._util import _empty_list_to_none +from .bbox import Bbox +from .cuboid import Cuboid +from .num import Num +from .object import Object +from .poly2d import Poly2d +from .poly3d import Poly3d +from .seg3d import Seg3d +from .sensor_reference import SensorReference + + +@dataclass +class Frame: + """A container of dynamic, timewise, information.""" + + timestamp: Decimal | None = None + "Timestamp containing the Unix epoch time of the frame with up to nanosecond precision." + + sensors: dict[str, SensorReference] = field(default_factory=dict) + "References to the sensors with frame specific information like timestamp and uri." + + frame_data: dict[str, Num] = field(default_factory=dict) + """Dictionary containing data directly connected to the frame and not to any object, like + gps/imu data. Dictionary keys are the ID-strings of the variable the data belongs to.""" + + annotations: dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d] = field(default_factory=dict) + "All annotations of this frame." + + @classmethod + def from_json(cls, json: JSONFrame) -> Frame: + """Construct an instant of this class from RailLabel JSON data.""" + return Frame( + timestamp=_timestamp_from_dict(json.frame_properties), + sensors=_sensors_from_dict(json.frame_properties), + frame_data=_frame_data_from_dict(json.frame_properties), + annotations=_annotations_from_json(json.objects), + ) + + def to_json(self, objects: dict[UUID, Object]) -> JSONFrame: + """Export this object into the RailLabel JSON format.""" + return JSONFrame( + frame_properties=JSONFrameProperties( + timestamp=self.timestamp, + streams={ + sensor_id: sensor_ref.to_json() for sensor_id, sensor_ref in self.sensors.items() + }, + frame_data=JSONFrameData(num=[num.to_json() for num in self.frame_data.values()]), + ), + objects=_objects_to_json(self.annotations, objects), + ) + + +def _timestamp_from_dict(frame_properties: JSONFrameProperties | None) -> Decimal | None: + if frame_properties is None: + return None + + if frame_properties.timestamp is None: + return None + + return Decimal(frame_properties.timestamp) + + +def _sensors_from_dict(frame_properties: JSONFrameProperties | None) -> dict[str, SensorReference]: + if frame_properties is None: + return {} + + if frame_properties.streams is None: + return {} + + return { + sensor_id: SensorReference.from_json(sensor_ref) + for sensor_id, sensor_ref in frame_properties.streams.items() + } + + +def _frame_data_from_dict(frame_properties: JSONFrameProperties | None) -> dict[str, Num]: + if frame_properties is None: + return {} + + if frame_properties.frame_data is None: + return {} + + if frame_properties.frame_data.num is None: + return {} + + return {num.name: Num.from_json(num) for num in frame_properties.frame_data.num} + + +def _annotations_from_json( + json_object_data: dict[UUID, JSONObjectData] | None, +) -> dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d]: + if json_object_data is None: + return {} + + annotations: dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d] = {} + + for object_id, object_data in json_object_data.items(): + for json_bbox in _resolve_none_to_empty_list(object_data.object_data.bbox): + annotations[json_bbox.uid] = Bbox.from_json(json_bbox, object_id) + + for json_cuboid in _resolve_none_to_empty_list(object_data.object_data.cuboid): + annotations[json_cuboid.uid] = Cuboid.from_json(json_cuboid, object_id) + + for json_poly2d in _resolve_none_to_empty_list(object_data.object_data.poly2d): + annotations[json_poly2d.uid] = Poly2d.from_json(json_poly2d, object_id) + + for json_poly3d in _resolve_none_to_empty_list(object_data.object_data.poly3d): + annotations[json_poly3d.uid] = Poly3d.from_json(json_poly3d, object_id) + + for json_seg3d in _resolve_none_to_empty_list(object_data.object_data.vec): + annotations[json_seg3d.uid] = Seg3d.from_json(json_seg3d, object_id) + + return annotations + + +def _resolve_none_to_empty_list(optional_list: list | None) -> list: + if optional_list is None: + return [] + return optional_list + + +def _objects_to_json( + annotations: dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d], objects: dict[UUID, Object] +) -> dict[str, JSONObjectData] | None: + if len(annotations) == 0: + return None + + object_data = {} + + for ann_id, annotation in annotations.items(): + object_id = str(annotation.object_id) + + if object_id not in object_data: + object_data[object_id] = JSONObjectData( + object_data=JSONAnnotations( + bbox=[], + cuboid=[], + poly2d=[], + poly3d=[], + vec=[], + ) + ) + + json_annotation = annotation.to_json(ann_id, objects[UUID(object_id)].type) + + if isinstance(annotation, Bbox): + object_data[object_id].object_data.bbox.append(json_annotation) # type: ignore + + elif isinstance(annotation, Cuboid): + object_data[object_id].object_data.cuboid.append(json_annotation) # type: ignore + + elif isinstance(annotation, Poly2d): + object_data[object_id].object_data.poly2d.append(json_annotation) # type: ignore + + elif isinstance(annotation, Poly3d): + object_data[object_id].object_data.poly3d.append(json_annotation) # type: ignore + + elif isinstance(annotation, Seg3d): + object_data[object_id].object_data.vec.append(json_annotation) # type: ignore + + else: + raise TypeError + + for object_id in object_data: + object_data[object_id].object_data.bbox = _empty_list_to_none( + object_data[object_id].object_data.bbox + ) + object_data[object_id].object_data.cuboid = _empty_list_to_none( + object_data[object_id].object_data.cuboid + ) + object_data[object_id].object_data.poly2d = _empty_list_to_none( + object_data[object_id].object_data.poly2d + ) + object_data[object_id].object_data.poly3d = _empty_list_to_none( + object_data[object_id].object_data.poly3d + ) + object_data[object_id].object_data.vec = _empty_list_to_none( + object_data[object_id].object_data.vec + ) + + return object_data diff --git a/raillabel/format/frame_interval.py b/raillabel/format/frame_interval.py new file mode 100644 index 0000000..6b9cd68 --- /dev/null +++ b/raillabel/format/frame_interval.py @@ -0,0 +1,82 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import JSONFrameInterval + + +@dataclass +class FrameInterval: + """Closed interval of frames.""" + + start: int + "Initial frame number of the interval (inclusive)." + + end: int + "Ending frame number of the interval (inclusive)." + + @classmethod + def from_json(cls, json: JSONFrameInterval) -> FrameInterval: + """Construct an instant of this class from RailLabel JSON data.""" + return FrameInterval( + start=json.frame_start, + end=json.frame_end, + ) + + @classmethod + def from_frame_ids(cls, frame_ids: list[int]) -> list[FrameInterval]: + """Convert a list of frame uids into FrameIntervals. + + Example: + ------- + ```python + FrameInterval.from_frame_ids([0, 1, 2, 3, 9, 12, 13, 14]) == [ + FrameInterval(0, 3), + FrameInterval(9, 9), + FrameInterval(12, 14), + ] + ``` + + """ + sorted_frame_ids = sorted(frame_ids) + frame_id_intervals = _slice_into_intervals(sorted_frame_ids) + + return [ + FrameInterval(start=interval[0], end=interval[-1]) for interval in frame_id_intervals + ] + + def to_json(self) -> JSONFrameInterval: + """Export this object into the RailLabel JSON format.""" + return JSONFrameInterval( + frame_start=self.start, + frame_end=self.end, + ) + + def __len__(self) -> int: + """Return the length in frames.""" + return abs(self.start - self.end) + 1 + + +def _slice_into_intervals(sorted_frame_ids: list[int]) -> list[list[int]]: + if len(sorted_frame_ids) == 0: + return [] + + if len(sorted_frame_ids) == 1: + return [sorted_frame_ids] + + intervals = [] + interval_start_i = 0 + for i, frame_id in enumerate(sorted_frame_ids[1:]): + previous_frame_id = sorted_frame_ids[i] + + if frame_id - previous_frame_id > 1: + intervals.append(sorted_frame_ids[interval_start_i : i + 1]) + interval_start_i = i + 1 + + intervals.append(sorted_frame_ids[interval_start_i : len(sorted_frame_ids)]) + interval_start_i = len(sorted_frame_ids) + + return intervals diff --git a/raillabel/format/gps_imu.py b/raillabel/format/gps_imu.py new file mode 100644 index 0000000..86ca56a --- /dev/null +++ b/raillabel/format/gps_imu.py @@ -0,0 +1,40 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from raillabel.json_format import JSONCoordinateSystem, JSONStreamOther + +from ._sensor_without_intrinsics import _extrinsics_from_json, _SensorWithoutIntrinsics + + +class GpsImu(_SensorWithoutIntrinsics): + """A gps sensor with inertial measurement unit.""" + + TYPE: str = "gps_imu" + + @classmethod + def from_json( + cls, json_stream: JSONStreamOther, json_coordinate_system: JSONCoordinateSystem + ) -> GpsImu: + """Construct an instant of this class from RailLabel JSON data.""" + return GpsImu( + extrinsics=_extrinsics_from_json(json_coordinate_system.pose_wrt_parent), + uri=json_stream.uri, + description=json_stream.description, + ) + + def to_json(self) -> tuple[JSONStreamOther, JSONCoordinateSystem]: + """Export this object into the RailLabel JSON format.""" + return ( + JSONStreamOther( + type="gps_imu", + uri=self.uri, + description=self.description, + ), + JSONCoordinateSystem( + parent="base", + type="sensor", + pose_wrt_parent=self.extrinsics.to_json() if self.extrinsics is not None else None, + ), + ) diff --git a/raillabel/format/intrinsics_pinhole.py b/raillabel/format/intrinsics_pinhole.py new file mode 100644 index 0000000..d0b8165 --- /dev/null +++ b/raillabel/format/intrinsics_pinhole.py @@ -0,0 +1,50 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import JSONIntrinsicsPinhole + + +@dataclass +class IntrinsicsPinhole: + """Intrinsic calibration for a camera sensor.""" + + camera_matrix: tuple[ + float, float, float, float, float, float, float, float, float, float, float, float + ] + """This is a 3x4 camera matrix which projects 3D homogeneous points (4x1) from a camera + coordinate system into the image plane (3x1). This is the usual K matrix for camera projection as + in OpenCV. It is extended from 3x3 to 3x4 to enable its direct utilisation to project 4x1 + homogeneous 3D points. The matrix is defined to follow the camera model: x-to-right, y-down, + z-forward. The following equation applies: x_img = camera_matrix * X_ccs.""" + + distortion: tuple[float, float, float, float, float] + "This is the array 1x5 radial and tangential distortion coefficients." + + width_px: int + "Width of the image frame in pixel." + + height_px: int + "Height of the image frame in pixel." + + @classmethod + def from_json(cls, json: JSONIntrinsicsPinhole) -> IntrinsicsPinhole: + """Construct an instant of this class from RailLabel JSON data.""" + return IntrinsicsPinhole( + camera_matrix=json.camera_matrix, + distortion=json.distortion_coeffs, + width_px=json.width_px, + height_px=json.height_px, + ) + + def to_json(self) -> JSONIntrinsicsPinhole: + """Export this object into the RailLabel JSON format.""" + return JSONIntrinsicsPinhole( + camera_matrix=self.camera_matrix, + distortion_coeffs=self.distortion, + width_px=self.width_px, + height_px=self.height_px, + ) diff --git a/raillabel/format/intrinsics_radar.py b/raillabel/format/intrinsics_radar.py new file mode 100644 index 0000000..579d6ae --- /dev/null +++ b/raillabel/format/intrinsics_radar.py @@ -0,0 +1,40 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import JSONIntrinsicsRadar + + +@dataclass +class IntrinsicsRadar: + """Intrinsic calibration for a radar sensor.""" + + resolution_px_per_m: float + """Factor for calculating the 3D coordinates of a pixel in the cartesian radar images. Number of + pixels in the images per meter from the sensor.""" + + width_px: int + "Width of the cartesian image frame in pixel." + + height_px: int + "Height of the cartesian image frame in pixel." + + @classmethod + def from_json(cls, json: JSONIntrinsicsRadar) -> IntrinsicsRadar: + """Construct an instant of this class from RailLabel JSON data.""" + return IntrinsicsRadar( + resolution_px_per_m=json.resolution_px_per_m, + width_px=json.width_px, + height_px=json.height_px, + ) + + def to_json(self) -> JSONIntrinsicsRadar: + """Export this object into the RailLabel JSON format.""" + return JSONIntrinsicsRadar( + resolution_px_per_m=self.resolution_px_per_m, + width_px=self.width_px, + height_px=self.height_px, + ) diff --git a/raillabel/format/lidar.py b/raillabel/format/lidar.py new file mode 100644 index 0000000..a3d4ae5 --- /dev/null +++ b/raillabel/format/lidar.py @@ -0,0 +1,40 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from raillabel.json_format import JSONCoordinateSystem, JSONStreamOther + +from ._sensor_without_intrinsics import _extrinsics_from_json, _SensorWithoutIntrinsics + + +class Lidar(_SensorWithoutIntrinsics): + """A lidar sensor.""" + + TYPE: str = "lidar" + + @classmethod + def from_json( + cls, json_stream: JSONStreamOther, json_coordinate_system: JSONCoordinateSystem + ) -> Lidar: + """Construct an instant of this class from RailLabel JSON data.""" + return Lidar( + extrinsics=_extrinsics_from_json(json_coordinate_system.pose_wrt_parent), + uri=json_stream.uri, + description=json_stream.description, + ) + + def to_json(self) -> tuple[JSONStreamOther, JSONCoordinateSystem]: + """Export this object into the RailLabel JSON format.""" + return ( + JSONStreamOther( + type="lidar", + uri=self.uri, + description=self.description, + ), + JSONCoordinateSystem( + parent="base", + type="sensor", + pose_wrt_parent=self.extrinsics.to_json() if self.extrinsics is not None else None, + ), + ) diff --git a/raillabel/format/metadata.py b/raillabel/format/metadata.py new file mode 100644 index 0000000..ad571ea --- /dev/null +++ b/raillabel/format/metadata.py @@ -0,0 +1,61 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import JSONMetadata + + +@dataclass +class Metadata: + """Container for metadata information about the scene itself.""" + + schema_version: str + "Version number of the OpenLABEL schema this annotation object follows." + + annotator: str | None = None + "Name or description of the annotator that created the annotations." + + comment: str | None = None + "Additional information or description about the annotation content." + + exporter_version: str | None = None + "Version of the raillabel-devkit, that last exported the scene." + + file_version: str | None = None + "Version number of the raillabel annotation content." + + name: str | None = None + "Name of the raillabel annotation content." + + subschema_version: str | None = None + "Version number of the RailLabel schema this annotation object follows." + + tagged_file: str | None = None + "Directory with the exported data_dict (e.g. images, point clouds)." + + @classmethod + def from_json(cls, json: JSONMetadata) -> Metadata: + """Construct an instant of this class from RailLabel JSON data.""" + metadata = Metadata( + schema_version=json.schema_version, + name=json.name, + subschema_version=json.subschema_version, + exporter_version=json.exporter_version, + file_version=json.file_version, + tagged_file=json.tagged_file, + annotator=json.annotator, + comment=json.comment, + ) + + if json.model_extra is not None: + for extra_field, extra_value in json.model_extra.items(): + setattr(metadata, extra_field, extra_value) + + return metadata + + def to_json(self) -> JSONMetadata: + """Export this object into the RailLabel JSON format.""" + return JSONMetadata(**dict(vars(self))) diff --git a/raillabel/format/num.py b/raillabel/format/num.py new file mode 100644 index 0000000..e77d1c0 --- /dev/null +++ b/raillabel/format/num.py @@ -0,0 +1,45 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.json_format import JSONNum + + +@dataclass +class Num: + """A number.""" + + name: str + "Human readable name describing the annotation." + + val: float + "This is the value of the number object." + + id: UUID | None = None + "The unique identifyer of the Num." + + sensor_id: str | None = None + "A reference to the sensor, this value is represented in." + + @classmethod + def from_json(cls, json: JSONNum) -> Num: + """Construct an instant of this class from RailLabel JSON data.""" + return Num( + name=json.name, + val=json.val, + id=json.uid, + sensor_id=json.coordinate_system, + ) + + def to_json(self) -> JSONNum: + """Export this object into the RailLabel JSON format.""" + return JSONNum( + name=self.name, + val=self.val, + coordinate_system=self.sensor_id, + uid=self.id, + ) diff --git a/raillabel/format/object.py b/raillabel/format/object.py new file mode 100644 index 0000000..6a3eaa3 --- /dev/null +++ b/raillabel/format/object.py @@ -0,0 +1,104 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING +from uuid import UUID + +from raillabel.json_format import JSONElementDataPointer, JSONFrameInterval, JSONObject + +from ._attributes import _attributes_to_json +from .frame_interval import FrameInterval + +if TYPE_CHECKING: + from .frame import Frame + + +@dataclass +class Object: + """Physical, unique object in the data, that can be tracked via its UID.""" + + name: str + """Name of the object. It is a friendly name and not used for indexing. Commonly the class name + is used followed by an underscore and an integer (i.e. person_0032).""" + + type: str + "The type of an object defines the class the object corresponds to (like 'person')." + + @classmethod + def from_json(cls, json: JSONObject) -> Object: + """Construct an instant of this class from RailLabel JSON data.""" + return Object( + name=json.name, + type=json.type, + ) + + def to_json(self, object_id: UUID, frames: dict[int, Frame]) -> JSONObject: + """Export this object into the RailLabel JSON format.""" + return JSONObject( + name=self.name, + type=self.type, + frame_intervals=_frame_intervals_to_json(object_id, frames), + object_data_pointers=_object_data_pointers_to_json(object_id, self.type, frames), + ) + + +def _frame_intervals_to_json(object_id: UUID, frames: dict[int, Frame]) -> list[JSONFrameInterval]: + frames_with_this_object = set() + + for frame_id, frame in frames.items(): + for annotation in frame.annotations.values(): + if annotation.object_id == object_id: + frames_with_this_object.add(frame_id) + continue + + return [fi.to_json() for fi in FrameInterval.from_frame_ids(list(frames_with_this_object))] + + +def _object_data_pointers_to_json( + object_id: UUID, object_type: str, frames: dict[int, Frame] +) -> dict[str, JSONElementDataPointer]: + pointers_raw = {} + + for frame_id, frame in frames.items(): + for annotation in [ann for ann in frame.annotations.values() if ann.object_id == object_id]: + annotation_name = annotation.name(object_type) + if annotation_name not in pointers_raw: + pointers_raw[annotation_name] = { + "frame_intervals": set(), + "type": annotation_name.split("__")[1], + "attribute_pointers": {}, + } + + pointers_raw[annotation_name]["frame_intervals"].add(frame_id) # type: ignore + json_attributes = _attributes_to_json(annotation.attributes) + + if json_attributes is None: + continue + + for attribute in json_attributes.boolean: # type: ignore + pointers_raw[annotation_name]["attribute_pointers"][attribute.name] = "boolean" # type: ignore + + for attribute in json_attributes.num: # type: ignore + pointers_raw[annotation_name]["attribute_pointers"][attribute.name] = "num" # type: ignore + + for attribute in json_attributes.text: # type: ignore + pointers_raw[annotation_name]["attribute_pointers"][attribute.name] = "text" # type: ignore + + for attribute in json_attributes.vec: # type: ignore + pointers_raw[annotation_name]["attribute_pointers"][attribute.name] = "vec" # type: ignore + + object_data_pointers = {} + for annotation_name, object_data_pointer in pointers_raw.items(): + object_data_pointers[annotation_name] = JSONElementDataPointer( + type=object_data_pointer["type"], + frame_intervals=[ + fi.to_json() + for fi in FrameInterval.from_frame_ids(list(object_data_pointer["frame_intervals"])) # type: ignore + ], + attribute_pointers=object_data_pointer["attribute_pointers"], + ) + + return object_data_pointers diff --git a/raillabel/format/other_sensor.py b/raillabel/format/other_sensor.py new file mode 100644 index 0000000..7d62518 --- /dev/null +++ b/raillabel/format/other_sensor.py @@ -0,0 +1,40 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from raillabel.json_format import JSONCoordinateSystem, JSONStreamOther + +from ._sensor_without_intrinsics import _extrinsics_from_json, _SensorWithoutIntrinsics + + +class OtherSensor(_SensorWithoutIntrinsics): + """A sensor that is not represented by the available options.""" + + TYPE: str = "other" + + @classmethod + def from_json( + cls, json_stream: JSONStreamOther, json_coordinate_system: JSONCoordinateSystem + ) -> OtherSensor: + """Construct an instant of this class from RailLabel JSON data.""" + return OtherSensor( + extrinsics=_extrinsics_from_json(json_coordinate_system.pose_wrt_parent), + uri=json_stream.uri, + description=json_stream.description, + ) + + def to_json(self) -> tuple[JSONStreamOther, JSONCoordinateSystem]: + """Export this object into the RailLabel JSON format.""" + return ( + JSONStreamOther( + type="other", + uri=self.uri, + description=self.description, + ), + JSONCoordinateSystem( + parent="base", + type="sensor", + pose_wrt_parent=self.extrinsics.to_json() if self.extrinsics is not None else None, + ), + ) diff --git a/raillabel/format/point2d.py b/raillabel/format/point2d.py new file mode 100644 index 0000000..d50b7bd --- /dev/null +++ b/raillabel/format/point2d.py @@ -0,0 +1,26 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Point2d: + """A 2d point in an image.""" + + x: float + "The x-coordinate of the point in the image in pixels from the left." + + y: float + "The y-coordinate of the point in the image in pixels from the top." + + @classmethod + def from_json(cls, json: tuple[float, float]) -> Point2d: + """Construct an instant of this class from RailLabel JSON data.""" + return Point2d(x=json[0], y=json[1]) + + def to_json(self) -> tuple[float, float]: + """Export this object into the RailLabel JSON format.""" + return (self.x, self.y) diff --git a/raillabel/format/point3d.py b/raillabel/format/point3d.py new file mode 100644 index 0000000..efca84a --- /dev/null +++ b/raillabel/format/point3d.py @@ -0,0 +1,29 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Point3d: + """A point in the 3D space.""" + + x: float + "The x-coordinate of the point." + + y: float + "The y-coordinate of the point." + + z: float + "The z-coordinate of the point." + + @classmethod + def from_json(cls, json: tuple[float, float, float]) -> Point3d: + """Construct an instant of this class from RailLabel JSON data.""" + return Point3d(x=json[0], y=json[1], z=json[2]) + + def to_json(self) -> tuple[float, float, float]: + """Export this object into the RailLabel JSON format.""" + return (self.x, self.y, self.z) diff --git a/raillabel/format/poly2d.py b/raillabel/format/poly2d.py new file mode 100644 index 0000000..ed20388 --- /dev/null +++ b/raillabel/format/poly2d.py @@ -0,0 +1,63 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.json_format import JSONPoly2d + +from ._attributes import _attributes_from_json, _attributes_to_json +from ._util import _flatten_list +from .point2d import Point2d + + +@dataclass +class Poly2d: + """Sequence of 2D points. Can either be a polygon or polyline.""" + + points: list[Point2d] + "List of the 2d points that make up the polyline." + + closed: bool + "If True, this object represents a polygon and if False, it represents a polyline." + + object_id: UUID + "The unique identifyer of the real-life object, this annotation belongs to." + + sensor_id: str + "The unique identifyer of the sensor this annotation is labeled in." + + attributes: dict[str, float | bool | str | list] + "Additional information associated with the annotation." + + @classmethod + def from_json(cls, json: JSONPoly2d, object_id: UUID) -> Poly2d: + """Construct an instant of this class from RailLabel JSON data.""" + return Poly2d( + points=[ + Point2d(x=float(json.val[i]), y=float(json.val[i + 1])) + for i in range(0, len(json.val), 2) + ], + closed=json.closed, + object_id=object_id, + sensor_id=json.coordinate_system, + attributes=_attributes_from_json(json.attributes), + ) + + def to_json(self, uid: UUID, object_type: str) -> JSONPoly2d: + """Export this object into the RailLabel JSON format.""" + return JSONPoly2d( + name=self.name(object_type), + val=_flatten_list([point.to_json() for point in self.points]), + closed=self.closed, + mode="MODE_POLY2D_ABSOLUTE", + coordinate_system=self.sensor_id, + uid=uid, + attributes=_attributes_to_json(self.attributes), + ) + + def name(self, object_type: str) -> str: + """Return the name of the annotation used for indexing in the object data pointers.""" + return f"{self.sensor_id}__poly2d__{object_type}" diff --git a/raillabel/format/poly3d.py b/raillabel/format/poly3d.py new file mode 100644 index 0000000..b49ff4e --- /dev/null +++ b/raillabel/format/poly3d.py @@ -0,0 +1,62 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.json_format import JSONPoly3d + +from ._attributes import _attributes_from_json, _attributes_to_json +from ._util import _flatten_list +from .point3d import Point3d + + +@dataclass +class Poly3d: + """Sequence of 3D points. Can either be a polygon or polyline.""" + + points: list[Point3d] + "List of the 3d points that make up the polyline." + + closed: bool + "If True, this object represents a polygon and if False, it represents a polyline." + + object_id: UUID + "The unique identifyer of the real-life object, this annotation belongs to." + + sensor_id: str + "The unique identifyer of the sensor this annotation is labeled in." + + attributes: dict[str, float | bool | str | list] + "Additional information associated with the annotation." + + @classmethod + def from_json(cls, json: JSONPoly3d, object_id: UUID) -> Poly3d: + """Construct an instant of this class from RailLabel JSON data.""" + return Poly3d( + points=[ + Point3d(x=float(json.val[i]), y=float(json.val[i + 1]), z=float(json.val[i + 2])) + for i in range(0, len(json.val), 3) + ], + closed=json.closed, + object_id=object_id, + sensor_id=json.coordinate_system, + attributes=_attributes_from_json(json.attributes), + ) + + def to_json(self, uid: UUID, object_type: str) -> JSONPoly3d: + """Export this object into the RailLabel JSON format.""" + return JSONPoly3d( + name=self.name(object_type), + val=_flatten_list([point.to_json() for point in self.points]), + closed=self.closed, + coordinate_system=self.sensor_id, + uid=uid, + attributes=_attributes_to_json(self.attributes), + ) + + def name(self, object_type: str) -> str: + """Return the name of the annotation used for indexing in the object data pointers.""" + return f"{self.sensor_id}__poly3d__{object_type}" diff --git a/raillabel/format/quaternion.py b/raillabel/format/quaternion.py new file mode 100644 index 0000000..a6a4df5 --- /dev/null +++ b/raillabel/format/quaternion.py @@ -0,0 +1,32 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Quaternion: + """A rotation represented by a quaternion.""" + + x: float + "The x component of the quaternion." + + y: float + "The y component of the quaternion." + + z: float + "The z component of the quaternion." + + w: float + "The omega component of the quaternion." + + @classmethod + def from_json(cls, json: tuple[float, float, float, float]) -> Quaternion: + """Construct an instant of this class from RailLabel JSON data.""" + return Quaternion(x=json[0], y=json[1], z=json[2], w=json[3]) + + def to_json(self) -> tuple[float, float, float, float]: + """Export this object into the RailLabel JSON format.""" + return (self.x, self.y, self.z, self.w) diff --git a/raillabel/format/radar.py b/raillabel/format/radar.py new file mode 100644 index 0000000..226c143 --- /dev/null +++ b/raillabel/format/radar.py @@ -0,0 +1,71 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import ( + JSONCoordinateSystem, + JSONStreamRadar, + JSONStreamRadarProperties, + JSONTransformData, +) + +from .intrinsics_radar import IntrinsicsRadar +from .transform import Transform + + +@dataclass +class Radar: + """A radar sensor.""" + + intrinsics: IntrinsicsRadar + "The intrinsic calibration of the sensor." + + extrinsics: Transform | None = None + "External calibration of the sensor defined by the 3D transform to the coordinate system origin." + + uri: str | None = None + "Name of the subdirectory containing the sensor files." + + description: str | None = None + "Additional information about the sensor." + + TYPE: str = "radar" + + @classmethod + def from_json( + cls, json_stream: JSONStreamRadar, json_coordinate_system: JSONCoordinateSystem + ) -> Radar: + """Construct an instant of this class from RailLabel JSON data.""" + return Radar( + intrinsics=IntrinsicsRadar.from_json(json_stream.stream_properties.intrinsics_radar), + extrinsics=_extrinsics_from_json(json_coordinate_system.pose_wrt_parent), + uri=json_stream.uri, + description=json_stream.description, + ) + + def to_json(self) -> tuple[JSONStreamRadar, JSONCoordinateSystem]: + """Export this object into the RailLabel JSON format.""" + return ( + JSONStreamRadar( + type="radar", + stream_properties=JSONStreamRadarProperties( + intrinsics_radar=self.intrinsics.to_json() + ), + uri=self.uri, + description=self.description, + ), + JSONCoordinateSystem( + parent="base", + type="sensor", + pose_wrt_parent=self.extrinsics.to_json() if self.extrinsics is not None else None, + ), + ) + + +def _extrinsics_from_json(json_transform: JSONTransformData | None) -> Transform | None: + if json_transform is None: + return None + return Transform.from_json(json_transform) diff --git a/raillabel/format/raillabel/__init__.py b/raillabel/format/raillabel/__init__.py deleted file mode 100644 index cf306c7..0000000 --- a/raillabel/format/raillabel/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 -"""Collection of all raillabel format classes.""" diff --git a/raillabel/format/raillabel/_object_annotation.py b/raillabel/format/raillabel/_object_annotation.py deleted file mode 100644 index ec49797..0000000 --- a/raillabel/format/raillabel/_object_annotation.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from abc import ABC, abstractmethod, abstractproperty -from dataclasses import dataclass, field -from importlib import import_module -from inspect import isclass -from pathlib import Path -from pkgutil import iter_modules - -from ..._util._attribute_type import AttributeType -from ..._util._warning import _warning -from .object import Object -from .sensor import Sensor - - -@dataclass -class _ObjectAnnotation(ABC): - - uid: str - object: Object - sensor: t.Optional[Sensor] = None - attributes: t.Dict[str, t.Union[int, float, bool, str, list]] = field(default_factory=dict) - - @property - def name(self) -> str: - if self.sensor is None: - raise AttributeError( - f"Annotation {self.uid} does not have a 'sensor', which is required " - + "to create the name." - ) - - return f"{self.sensor.uid}__{self.OPENLABEL_ID}__{self.object.type}" - - @property - @abstractproperty - def _REQ_FIELDS(self) -> t.List[str]: - raise NotImplementedError - - @property - @abstractproperty - def OPENLABEL_ID(self) -> t.List[str]: - raise NotImplementedError - - # === Public Methods ===================================================== - - @abstractmethod - def asdict(self) -> t.Dict: - raise NotImplementedError - - @classmethod - @abstractmethod - def fromdict( - cls, - data_dict: t.Dict, - sensors: t.Dict, - object: Object, - ) -> t.Type["_ObjectAnnotation"]: - raise NotImplementedError - - # === Private Methods ==================================================== - - def _annotation_required_fields_asdict(self) -> t.Dict: - """Return the required fields from the parent class to dict.""" - return { - "uid": str(self.uid), - "name": str(self.name), - } - - def _annotation_optional_fields_asdict(self) -> t.Dict: - """Return the optional fields from the parent class to dict.""" - - dict_repr = {} - - if self.sensor is not None: - dict_repr["coordinate_system"] = str(self.sensor.uid) - - if self.attributes != {}: - dict_repr["attributes"] = self._attributes_asdict(self.attributes) - - return dict_repr - - def _attributes_asdict(self, attributes: dict) -> dict: - attributes_dict = {} - - for attr_name, attr_value in attributes.items(): - - attr_type = AttributeType.from_value(type(attr_value)).value - - if attr_type not in attributes_dict: - attributes_dict[attr_type] = [] - - attributes_dict[attr_type].append({"name": attr_name, "val": attr_value}) - - return attributes_dict - - @classmethod - def _coordinate_system_fromdict(cls, data_dict: dict, sensors: dict) -> t.Optional[Sensor]: - - is_coordinate_system_in_data = ( - "coordinate_system" in data_dict and data_dict["coordinate_system"] != "" - ) - - if not is_coordinate_system_in_data: - return None - - if data_dict["coordinate_system"] not in sensors: - _warning( - f"'{data_dict['coordinate_system']}' does not exist as a sensor, " - + f"but is referenced for the annotation {data_dict['uid']}." - ) - return None - - return sensors[data_dict["coordinate_system"]] - - @classmethod - def _attributes_fromdict( - cls, - data_dict: dict, - ) -> t.Dict[str, t.Union[int, float, bool, str, list]]: - - if "attributes" not in data_dict: - return {} - - return {a["name"]: a["val"] for l in data_dict["attributes"].values() for a in l} - - # === Special Methods ==================================================== - - def __post_init__(self): - """Check for required arguments after __init__. - - Inheritance in dataclasses has the flaw, that when the parent class has fields with - defaults and the child class has fields without, a TypeError is raised due to required - fields following optional ones. This is solved by making all fields in the child - optional and checking for required fields in __post_init__(). - - Raises - ------ - TypeError - If a required field has not been set. - """ - - for f in self._REQ_FIELDS: - if getattr(self, f) is None: - raise TypeError(f"{f} is a required argument for {self.__class__.__name__}") - - -def annotation_classes() -> t.Dict[str, t.Type[_ObjectAnnotation]]: - """Return dictionary with _Annotation child classes.""" - return ANNOTATION_CLASSES - - -def _collect_annotation_classes(): - """Collect annotation child classes and store them.""" - - global ANNOTATION_CLASSES - - package_dir = str(Path(__file__).resolve().parent) - for _, module_name, _ in iter_modules([package_dir]): - - module = import_module(f"raillabel.format.raillabel.{module_name}") - for attribute_name in dir(module): - attribute = getattr(module, attribute_name) - - if ( - isclass(attribute) - and issubclass(attribute, _ObjectAnnotation) - and attribute != _ObjectAnnotation - ): - ANNOTATION_CLASSES[attribute.OPENLABEL_ID] = attribute - - -ANNOTATION_CLASSES = {} -_collect_annotation_classes() diff --git a/raillabel/format/raillabel/bbox.py b/raillabel/format/raillabel/bbox.py deleted file mode 100644 index 3b254b2..0000000 --- a/raillabel/format/raillabel/bbox.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - -from ._object_annotation import _ObjectAnnotation -from .object import Object -from .point2d import Point2d -from .size2d import Size2d - - -@dataclass -class Bbox(_ObjectAnnotation): - """A 2D bounding box in an image. - - Parameters - ---------- - uid: str - This a string representing the unique universal identifier of the annotation. - pos: raillabel.format.Point2d - The center point of the bbox in pixels. - size: raillabel.format.Size2d - The dimensions of the bbox in pixels from the top left corner to the bottom right corner. - object: raillabel.format.Object - A reference to the object, this annotation belongs to. - sensor: raillabel.format.Sensor, optional - A reference to the sensor, this annotation is labeled in. Default is None. - attributes: dict, optional - Attributes of the annotation. Dict keys are the name str of the attribute, values are the - attribute values. Default is {}. - - Properties (read-only) - ---------------------- - name: str - Name of the annotation used by the VCD player for indexing in the object data pointers. - """ - - pos: Point2d = None - size: Size2d = None - - OPENLABEL_ID = "bbox" - _REQ_FIELDS = ["pos", "size"] - - @classmethod - def fromdict(cls, data_dict: dict, sensors: dict, object: Object) -> "Bbox": - """Generate a Bbox object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - sensors: dict - Dictionary containing all sensors for the scene. - object: raillabel.format.Object - Object this annotation belongs to. - - Returns - ------- - annotation: Bbox - Converted annotation. - """ - - return Bbox( - uid=str(data_dict["uid"]), - pos=Point2d(x=data_dict["val"][0], y=data_dict["val"][1]), - size=Size2d(x=data_dict["val"][2], y=data_dict["val"][3]), - object=object, - sensor=cls._coordinate_system_fromdict(data_dict, sensors), - attributes=cls._attributes_fromdict(data_dict), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = self._annotation_required_fields_asdict() - - dict_repr["val"] = [ - float(self.pos.x), - float(self.pos.y), - float(self.size.x), - float(self.size.y), - ] - - dict_repr.update(self._annotation_optional_fields_asdict()) - - return dict_repr diff --git a/raillabel/format/raillabel/cuboid.py b/raillabel/format/raillabel/cuboid.py deleted file mode 100644 index f4f88ff..0000000 --- a/raillabel/format/raillabel/cuboid.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - -from ._object_annotation import _ObjectAnnotation -from .object import Object -from .point3d import Point3d -from .quaternion import Quaternion -from .size3d import Size3d - - -@dataclass -class Cuboid(_ObjectAnnotation): - """3D bounding box. - - Parameters - ---------- - uid: str - This a string representing the unique universal identifier of the annotation. - pos: raillabel.format.Point3d - The center position of the cuboid in meters, where the x coordinate points ahead of the - vehicle, y points to the left and z points upwards. - quat: raillabel.format.Quaternion - The rotation of the cuboid in quaternions. - size: raillabel.format.Size3d - The size of the cuboid in meters. - object: raillabel.format.Object - A reference to the object, this annotation belongs to. - sensor: raillabel.format.Sensor, optional - A reference to the sensor, this annotation is labeled in. Default is None. - attributes: dict, optional - Attributes of the annotation. Dict keys are the name str of the attribute, values are the - attribute values. Default is {}. - - Properties (read-only) - ---------------------- - name: str - Name of the annotation used by the VCD player for indexing in the object data pointers. - """ - - pos: Point3d = None - quat: Quaternion = None - size: Size3d = None - - OPENLABEL_ID = "cuboid" - _REQ_FIELDS = ["pos", "size", "quat"] - - @classmethod - def fromdict(cls, data_dict: dict, sensors: dict, object: Object) -> "Cuboid": - """Generate a Cuboid object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - sensors: dict - Dictionary containing all sensors for the scene. - object: raillabel.format.Object - Object this annotation belongs to. - - Returns - ------- - annotation: Cuboid - Converted annotation. - """ - - return Cuboid( - uid=str(data_dict["uid"]), - pos=Point3d( - x=data_dict["val"][0], - y=data_dict["val"][1], - z=data_dict["val"][2], - ), - quat=Quaternion( - x=data_dict["val"][3], - y=data_dict["val"][4], - z=data_dict["val"][5], - w=data_dict["val"][6], - ), - size=Size3d( - x=data_dict["val"][7], - y=data_dict["val"][8], - z=data_dict["val"][9], - ), - object=object, - sensor=cls._coordinate_system_fromdict(data_dict, sensors), - attributes=cls._attributes_fromdict(data_dict), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = self._annotation_required_fields_asdict() - - dict_repr["val"] = [ - float(self.pos.x), - float(self.pos.y), - float(self.pos.z), - float(self.quat.x), - float(self.quat.y), - float(self.quat.z), - float(self.quat.w), - float(self.size.x), - float(self.size.y), - float(self.size.z), - ] - - dict_repr.update(self._annotation_optional_fields_asdict()) - - return dict_repr diff --git a/raillabel/format/raillabel/element_data_pointer.py b/raillabel/format/raillabel/element_data_pointer.py deleted file mode 100644 index f0e03cd..0000000 --- a/raillabel/format/raillabel/element_data_pointer.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from ..._util._attribute_type import AttributeType -from .frame_interval import FrameInterval - - -@dataclass -class ElementDataPointer: - """Container of pointers for annotations indexed by the "name" property. - - Used for indexing annotations for easy referencing without loading all of them. - - Parameters - ---------- - uid: str - Unique identifier of the ElementDataPointer assembled using a specific schema. - frame_intervals: list[raillabel.format.FrameInterval] - Frame intervals, this element data pointer is contained in. - attribute_pointers: dict[str, raillabel.util._attribute_type.AttributeType] - References of attributes contained in the referenced annotations with attribute type. - - Properties (read-only) - ---------------------- - uid: str - Unique identifier of the ElementDataPointer built from the attributes. - """ - - uid: str - frame_intervals: t.List[FrameInterval] - attribute_pointers: t.Dict[str, AttributeType] - - @property - def annotation_type(self) -> str: - """Return type of annotation e.g. bbox, cuboid.""" - return self.uid.split("__")[1] - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return { - "type": self.annotation_type, - "frame_intervals": self._frame_intervals_asdict(), - "attribute_pointers": self._attribute_pointers_asdict(), - } - - def _frame_intervals_asdict(self) -> t.List[t.Dict[str, int]]: - return [fi.asdict() for fi in self.frame_intervals] - - def _attribute_pointers_asdict(self) -> t.Dict[str, str]: - return { - attr_name: attr_type.value for attr_name, attr_type in self.attribute_pointers.items() - } diff --git a/raillabel/format/raillabel/frame.py b/raillabel/format/raillabel/frame.py deleted file mode 100644 index 575dd1f..0000000 --- a/raillabel/format/raillabel/frame.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import decimal -import typing as t -import uuid -from dataclasses import dataclass, field - -from ..._util._warning import _warning -from ._object_annotation import _ObjectAnnotation, annotation_classes -from .num import Num -from .object import Object -from .sensor import Sensor -from .sensor_reference import SensorReference - - -@dataclass -class Frame: - """A container of dynamic, timewise, information. - - Parameters - ---------- - uid: int - Number of the frame within the annotation file. Must be unique. - timestamp: decimal.Decimal, optional - Timestamp containing the Unix epoch time of the frame with up to nanosecond precision. - sensors: dict of raillabel.format.SensorReference, optional - References to the sensors with frame specific information like timestamp and uri. - Default is {}. - frame_data: dict, optional - Dictionary containing data directly connected to the frame and not to any object, like - gps/imu data. Dictionary keys are the ID-strings of the variable the data belongs to. - Default is {}. - annotations: dict[str, _ObjectAnnotation subclass], optional - Dictionary containing all annotations of this frame. Keys are annotation uids. - - Read-Only Attributes - -------------------- - object_data: dict[str, dict[str, _ObjectAnnotation subclass]] - Annotations categorized by object. Keys are object uids and values are the annotations - as a dict, that are part of the object. - """ - - uid: int - timestamp: t.Optional[decimal.Decimal] = None - sensors: t.Dict[str, SensorReference] = field(default_factory=dict) - frame_data: t.Dict[str, Num] = field(default_factory=dict) - annotations: t.Dict[str, t.Type[_ObjectAnnotation]] = field(default_factory=dict) - - @property - def object_data(self) -> t.Dict[str, t.Dict[str, t.Type[_ObjectAnnotation]]]: - """Return annotations categorized by Object-Id. - - Returns - ------- - dict[str, dict[UUID, _ObjectAnnotation subclass]] - Dictionary of annotations. Keys are object uids and values are annotations, that are - contained in the object. - """ - - object_data = {} - for ann_id, annotation in self.annotations.items(): - if annotation.object.uid not in object_data: - object_data[annotation.object.uid] = {} - - object_data[annotation.object.uid][ann_id] = annotation - - return object_data - - @classmethod - def fromdict( - cls, - uid: str, - data_dict: dict, - objects: t.Dict[str, Object], - sensors: t.Dict[str, Sensor], - ) -> "Frame": - """Generate a Frame object from a dict. - - Parameters - ---------- - uid: str - Unique identifier of the frame. - data_dict: dict - RailLabel format snippet containing the relevant data. - objects: dict - Dictionary of all objects in the scene. - sensors: dict - Dictionary of all sensors in the scene. - - Returns - ------- - frame: raillabel.format.Frame - Converted Frame object. - """ - - frame = Frame( - uid=int(uid), - timestamp=cls._timestamp_fromdict(data_dict), - sensors=cls._sensors_fromdict(data_dict, int(uid), sensors), - frame_data=cls._frame_data_fromdict(data_dict, sensors), - annotations=cls._objects_fromdict(data_dict, int(uid), objects, sensors), - ) - - frame = cls._fix_sensor_uri_attribute(frame) - return frame - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = {} - - if self.timestamp is not None or self.sensors != {} or self.frame_data != {}: - dict_repr["frame_properties"] = {} - - if self.timestamp is not None: - dict_repr["frame_properties"]["timestamp"] = str(self.timestamp) - - if self.sensors != {}: - dict_repr["frame_properties"]["streams"] = { - str(k): v.asdict() for k, v in self.sensors.items() - } - - if self.frame_data != {}: - dict_repr["frame_properties"]["frame_data"] = { - "num": [v.asdict() for v in self.frame_data.values()] - } - - if self.annotations != {}: - dict_repr["objects"] = self._annotations_asdict() - - return dict_repr - - @classmethod - def _timestamp_fromdict(cls, data_dict: dict) -> t.Optional[decimal.Decimal]: - - if "frame_properties" not in data_dict or "timestamp" not in data_dict["frame_properties"]: - return None - - return decimal.Decimal(data_dict["frame_properties"]["timestamp"]) - - @classmethod - def _sensors_fromdict( - cls, data_dict: dict, frame_uid: int, scene_sensors: t.Dict[str, Sensor] - ) -> t.Dict[str, SensorReference]: - - if "frame_properties" not in data_dict or "streams" not in data_dict["frame_properties"]: - return {} - - sensors = {} - - for sensor_id, sensor_dict in data_dict["frame_properties"]["streams"].items(): - if sensor_id not in scene_sensors: - _warning( - f"{sensor_id} does not exist as a stream, but is referenced in the " - + f"sync of frame {frame_uid}." - ) - continue - - sensors[sensor_id] = SensorReference.fromdict( - data_dict=sensor_dict, sensor=scene_sensors[sensor_id] - ) - - return sensors - - @classmethod - def _frame_data_fromdict( - cls, data_dict: dict, sensors: t.Dict[str, Sensor] - ) -> t.Dict[str, Num]: - - if "frame_properties" not in data_dict or "frame_data" not in data_dict["frame_properties"]: - return {} - - frame_data = {} - for ann_type in data_dict["frame_properties"]["frame_data"]: - for ann_raw in data_dict["frame_properties"]["frame_data"][ann_type]: - frame_data[ann_raw["name"]] = Num.fromdict(ann_raw, sensors) - - return frame_data - - @classmethod - def _objects_fromdict( - cls, - data_dict: dict, - frame_id: int, - objects: t.Dict[str, Object], - sensors: t.Dict[str, Sensor], - ) -> t.Dict[uuid.UUID, t.Type[_ObjectAnnotation]]: - - if "objects" not in data_dict: - return {} - - annotations = {} - - for obj_id, obj_ann in data_dict["objects"].items(): - - if obj_id not in objects: - _warning( - f"{obj_id} does not exist as an object, but is referenced in the object" - + f" annotation of frame {frame_id}." - ) - continue - - object_annotations = cls._object_annotations_fromdict( - data_dict=obj_ann["object_data"], - object=objects[obj_id], - sensors=sensors, - ) - - for annotation in object_annotations: - if annotation.uid in annotations: - cls._issue_duplicate_annotation_uid_warning(annotation.uid, frame_id) - annotation.uid = str(uuid.uuid4()) - - annotations[annotation.uid] = annotation - - return annotations - - @classmethod - def _object_annotations_fromdict( - cls, - data_dict: dict, - object: Object, - sensors: t.Dict[str, Sensor], - ) -> t.Iterator[t.Type[_ObjectAnnotation]]: - - for ann_type, annotations_raw in data_dict.items(): - for ann_raw in annotations_raw: - - ann_raw = cls._fix_deprecated_annotation_name(ann_raw, ann_type, object.type) - - yield annotation_classes()[ann_type].fromdict(ann_raw, sensors, object) - - @classmethod - def _fix_sensor_uri_attribute(cls, frame: "Frame") -> "Frame": - - for ann_id, ann in list(frame.annotations.items()): - for attr_name, attr_val in ann.attributes.items(): - - if attr_name != "uri": - continue - - _warning( - f"Deprecated attribute 'uri' detected in annotation {ann_id}. The error has" - + " been fixed. Please update the file via 'raillabel.save()'." - ) - - frame.sensors[ann.sensor.uid].uri = attr_val - del frame.annotations[ann_id].attributes[attr_name] - break - - return frame - - @classmethod - def _fix_deprecated_annotation_name(cls, ann_raw: dict, ann_type: str, obj_type: str) -> dict: - - if "uid" not in ann_raw: - try: - ann_raw["uid"] = str(uuid.UUID(ann_raw["name"])) - except ValueError: - ann_raw["uid"] = str(uuid.uuid4()) - - ann_raw["name"] = f"{ann_raw['coordinate_system']}__{ann_type}__{obj_type}" - - return ann_raw - - @classmethod - def _issue_duplicate_annotation_uid_warning(cls, ann_uid: str, frame_id: int): - _warning( - f"Annotation UID '{ann_uid}' is contained more than once in frame {frame_id}. " - + "A new uid will be assigned." - ) - - def _annotations_asdict(self) -> dict: - annotations_dict = {} - for object_id, annotations in self.object_data.items(): - annotations_dict[object_id] = {"object_data": {}} - - for annotation in annotations.values(): - if annotation.OPENLABEL_ID not in annotations_dict[object_id]["object_data"]: - annotations_dict[object_id]["object_data"][annotation.OPENLABEL_ID] = [] - - annotations_dict[object_id]["object_data"][annotation.OPENLABEL_ID].append( - annotation.asdict() - ) - - return annotations_dict - - def __eq__(self, other) -> bool: - """Handel equal comparisons.""" - - if not hasattr(other, "__dict__"): - return False - - if len(self.__dict__) != len(other.__dict__): - return False - - for attr in self.__dict__: - - if type(getattr(self, attr)) == type(self): - if getattr(self, attr).uid != getattr(other, attr).uid: - return False - - else: - if getattr(self, attr) != getattr(other, attr): - return False - - return True diff --git a/raillabel/format/raillabel/frame_interval.py b/raillabel/format/raillabel/frame_interval.py deleted file mode 100644 index 5d5d728..0000000 --- a/raillabel/format/raillabel/frame_interval.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - - -@dataclass -class FrameInterval: - """Closed interval of frames. - - Parameters - ---------- - frame_start: int - Initial frame number of the interval (inclusive). - frame_end: int - Ending frame number of the interval (inclusive). - """ - - frame_start: int - frame_end: int - - @classmethod - def fromdict(cls, data_dict: dict) -> "FrameInterval": - """Generate a FrameInterval object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - """ - - return FrameInterval( - frame_start=data_dict["frame_start"], - frame_end=data_dict["frame_end"], - ) - - @classmethod - def from_frame_uids(cls, frame_uids: t.List[int]) -> t.List["FrameInterval"]: - """Convert a list of frame uids into FrameIntervals. - - Parameters - ---------- - frame_uids: list[int] - List of frame uids, that should be included in the FrameIntervals. - - Returns - ------- - list[FrameInterval] - FrameIntervals corresponding to the frames ids. - - Example - ------- - FrameInterval.from_frame_uids([0, 1, 2, 3, 6, 7, 9, 12, 13, 14]) == [ - FrameInterval(0, 3), - FrameInterval(6, 7), - FrameInterval(9, 9), - FrameInterval(12, 14), - ] - """ - - sorted_frame_uids = sorted(frame_uids) - frame_uid_intervals = cls._slice_into_intervals(sorted_frame_uids) - - frame_intervals = [] - for interval in frame_uid_intervals: - frame_intervals.append(FrameInterval(frame_start=interval[0], frame_end=interval[-1])) - - return frame_intervals - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return { - "frame_start": int(self.frame_start), - "frame_end": int(self.frame_end), - } - - def __len__(self) -> int: - """Return the length in frames.""" - return abs(self.frame_start - self.frame_end) + 1 - - @classmethod - def _slice_into_intervals(cls, sorted_frame_uids: t.List[int]) -> t.List[t.List[int]]: - - if len(sorted_frame_uids) == 0: - return [] - - if len(sorted_frame_uids) == 1: - return [sorted_frame_uids] - - intervals = [] - interval_start_i = 0 - for i, frame_uid in enumerate(sorted_frame_uids[1:]): - previous_frame_uid = sorted_frame_uids[i] - - if frame_uid - previous_frame_uid > 1: - intervals.append(sorted_frame_uids[interval_start_i : i + 1]) - interval_start_i = i + 1 - - intervals.append(sorted_frame_uids[interval_start_i : len(sorted_frame_uids)]) - interval_start_i = len(sorted_frame_uids) - - return intervals diff --git a/raillabel/format/raillabel/intrinsics_pinhole.py b/raillabel/format/raillabel/intrinsics_pinhole.py deleted file mode 100644 index 4ce1a09..0000000 --- a/raillabel/format/raillabel/intrinsics_pinhole.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - - -@dataclass -class IntrinsicsPinhole: - """Intrinsic calibration for a camera sensor. - - Parameters - ---------- - camera_matrix: tuple of float of length 12 - This is a 3x4 camera matrix which projects 3D homogeneous points (4x1) from a camera - coordinate system into the image plane (3x1). This is the usual K matrix for camera - projection as in OpenCV. It is extended from 3x3 to 3x4 to enable its direct utilisation to - project 4x1 homogeneous 3D points. The matrix is defined to follow the camera model: - x-to-right, y-down, z-forward. The following equation applies: - x_img = camera_matrix * X_ccs. - distortion: tuple of float of length 5 to 14 - This is the array 1xN radial and tangential distortion coefficients. - width_px: int - Width of the image frame in pixels. - height_px: int - Height of the image frame in pixels. - """ - - camera_matrix: t.Tuple[float, ...] - distortion: t.Tuple[float, ...] - width_px: int - height_px: int - - @classmethod - def fromdict(cls, data_dict: dict) -> "IntrinsicsPinhole": - """Generate a IntrinsicsPinhole object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - - Returns - ------- - raillabel.format.IntrinsicsPinhole - Converted IntrinsicsPinhole object. - """ - - return IntrinsicsPinhole( - camera_matrix=tuple(data_dict["camera_matrix"]), - distortion=tuple(data_dict["distortion_coeffs"]), - width_px=data_dict["width_px"], - height_px=data_dict["height_px"], - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = { - "camera_matrix": list(self.camera_matrix), - "distortion_coeffs": list(self.distortion), - "width_px": int(self.width_px), - "height_px": int(self.height_px), - } - - return dict_repr diff --git a/raillabel/format/raillabel/intrinsics_radar.py b/raillabel/format/raillabel/intrinsics_radar.py deleted file mode 100644 index 63c9acb..0000000 --- a/raillabel/format/raillabel/intrinsics_radar.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class IntrinsicsRadar: - """Intrinsic calibration for a radar sensor. - - Parameters - ---------- - resolution_px_per_m: float - Factor for calculating the 3D coordinates of a pixel in the cartesian radar images. - Number of pixels in the images per meter from the sensor. - width_px: int - Width of the cartesian image frame in pixels. - height_px: int - Height of the cartesian image frame in pixels. - """ - - resolution_px_per_m: float - width_px: int - height_px: int - - @classmethod - def fromdict(cls, data_dict: dict) -> "IntrinsicsRadar": - """Generate a IntrinsicsRadar object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - - Returns - ------- - raillabel.format.IntrinsicsRadar - Converted IntrinsicsRadar object. - """ - - return IntrinsicsRadar( - resolution_px_per_m=data_dict["resolution_px_per_m"], - width_px=data_dict["width_px"], - height_px=data_dict["height_px"], - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return { - "resolution_px_per_m": float(self.resolution_px_per_m), - "width_px": int(self.width_px), - "height_px": int(self.height_px), - } diff --git a/raillabel/format/raillabel/metadata.py b/raillabel/format/raillabel/metadata.py deleted file mode 100644 index 3c06667..0000000 --- a/raillabel/format/raillabel/metadata.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from importlib import metadata as importlib_metadata - - -@dataclass -class Metadata: - """Container for metadata information about the scene itself. - - As the OpenLABEL metadata object accepts additional properties, so does this class. Any - properties present in the JSON will be added to the Metadata() object when read through - Metadata.fromdict(). Conversely, all attributes from the Metadata() object will be stored - into the JSON when using Metadata.asdict(). You can therefore just add attributes to the - Python object and have them stored. - Example: - m = Metadata.fromdict( - { - "schema_version": "1.0.0", - "some_additional_property": "Some Value" - } - ) - m.another_additional_property = "Another Value" - m.asdict() - -> { - "schema_version": "1.0.0", - "some_additional_property": "Some Value", - "another_additional_property": "Another Value" - } - - Parameters - ---------- - schema_version: str - Version number of the OpenLABEL schema this annotation object follows. - annotator: str, optional - Name or description of the annotator that created the annotations. Default is None. - comment: str, optional - Additional information or description about the annotation content. Default is None. - exporter_version: str, optional - Version of the raillabel-devkit, that last exported the scene. Default is None. - file_version: str, optional - Version number of the raillabel annotation content. Default is None. - name: str, optional - Name of the raillabel annotation content. Default is None. - subschema_version: str, optional - Version number of the RailLabel schema this annotation object follows. Default is None. - tagged_file: str, optional - Directory with the exported data_dict (e.g. images, point clouds). Default is None. - """ - - schema_version: str - annotator: t.Optional[str] = None - comment: t.Optional[str] = None - exporter_version: t.Optional[str] = None - file_version: t.Optional[str] = None - name: t.Optional[str] = None - subschema_version: t.Optional[str] = None - tagged_file: t.Optional[str] = None - - @classmethod - def fromdict(cls, data_dict: dict, subschema_version: t.Optional[str] = None) -> "Metadata": - """Generate a Metadata object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. Additional (non-defined) - arguments can be set and will be added as properties to Metadata. - subschema_version: str, optional - Version of the RailLabel subschema - - Returns - ------- - metadata: Metadata - Converted metadata. - """ - - metadata = Metadata( - schema_version=data_dict["schema_version"], - subschema_version=subschema_version, - exporter_version=cls._collect_exporter_version(), - ) - - return cls._set_additional_attributes(metadata, data_dict) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - """ - - return self._remove_empty_fields(vars(self)) - - @classmethod - def _collect_exporter_version(cls) -> t.Optional[str]: - - try: - exporter_version = importlib_metadata.version("raillabel") - except importlib_metadata.PackageNotFoundError: - return None - - version_number_length = len(exporter_version) - len(exporter_version.split(".")[-1]) - return exporter_version[: version_number_length - 1] - - @classmethod - def _set_additional_attributes(cls, metadata: "Metadata", data_dict: dict) -> "Metadata": - - PRESET_KEYS = ["schema_version", "subschema_version", "exporter_version"] - - for key, value in data_dict.items(): - if key in PRESET_KEYS: - continue - - is_key_a_valid_python_attribute = isinstance(key, str) and key.isidentifier() - - if not is_key_a_valid_python_attribute: - raise KeyError(f"'{key}' is not a valid python attribute") - - setattr(metadata, key, value) - - return metadata - - def _remove_empty_fields(self, dict_repr: dict) -> dict: - """Remove empty fields from a dictionary.""" - return {k: v for k, v in dict_repr.items() if v is not None} diff --git a/raillabel/format/raillabel/num.py b/raillabel/format/raillabel/num.py deleted file mode 100644 index 509d938..0000000 --- a/raillabel/format/raillabel/num.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from ..._util._warning import _warning -from .sensor import Sensor - - -@dataclass -class Num: - """A number. - - Parameters - ---------- - uid: str - This a string representing the unique universal identifier of the annotation. - name: str - Human readable name describing the annotation. - val: int or float - This is the value of the number object. - attributes: dict, optional - Attributes of the annotation. Dict keys are the uid str of the attribute, values are the - attribute values. Default is {}. - sensor: raillabel.format.Sensor, optional - A reference to the sensor, this value is represented in. Default is None. - """ - - uid: str - name: str - val: t.Union[int, float] - sensor: Sensor = None - - @classmethod - def fromdict(cls, data_dict: dict, sensors: dict) -> "Num": - """Generate a Num object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - sensors: dict - Dictionary containing all sensors for the scene. - - Returns - ------- - annotation: Num - Converted annotation. - """ - - return Num( - uid=str(data_dict["uid"]), - name=str(data_dict["name"]), - val=data_dict["val"], - sensor=cls._coordinate_system_fromdict(data_dict, sensors), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return { - "uid": str(self.uid), - "name": str(self.name), - "val": self.val, - "coordinate_system": str(self.sensor.uid), - } - - @classmethod - def _coordinate_system_fromdict(cls, data_dict: dict, sensors: dict) -> t.Optional[Sensor]: - - is_coordinate_system_in_data = ( - "coordinate_system" in data_dict and data_dict["coordinate_system"] != "" - ) - - if not is_coordinate_system_in_data: - return None - - if data_dict["coordinate_system"] not in sensors: - _warning( - f"'{data_dict['coordinate_system']}' does not exist as a sensor, " - + f"but is referenced for the annotation {data_dict['uid']}." - ) - return None - - return sensors[data_dict["coordinate_system"]] diff --git a/raillabel/format/raillabel/object.py b/raillabel/format/raillabel/object.py deleted file mode 100644 index b0649a4..0000000 --- a/raillabel/format/raillabel/object.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from _collections_abc import dict_values - -from .element_data_pointer import AttributeType, ElementDataPointer -from .frame_interval import FrameInterval - -if t.TYPE_CHECKING: - from .frame import Frame - - -@dataclass -class Object: - """Physical, unique object in the data, that can be tracked via its UID. - - Parameters - ---------- - uid: str - This a string representing the unique universal identifier for the object. - name: str - Name of the object. It is a friendly name and not used for indexing. Commonly the class - name is used followed by an underscore and an integer (i.e. person_0032). - type: str - The type of an object defines the class the object corresponds to. - """ - - uid: str - name: str - type: str - - # --- Public Methods -------------- - - @classmethod - def fromdict(cls, data_dict: dict, object_uid: str) -> "Object": - """Generate a Object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - object_uid: str - Unique identifier of the object. - - Returns - ------- - object: raillabel.format.Object - Converted object. - """ - return Object(uid=object_uid, type=data_dict["type"], name=data_dict["name"]) - - def asdict(self, frames: t.Optional[t.Dict[int, "Frame"]] = None) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - frames: dict, optional - The dictionary of frames stored under Scene.frames used for the frame intervals and - object data pointers. If None, these are not provided. Default is None. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - if frames is None: - return {"name": str(self.name), "type": str(self.type)} - - else: - return { - "name": str(self.name), - "type": str(self.type), - "frame_intervals": self._frame_intervals_asdict(self.frame_intervals(frames)), - "object_data_pointers": self._object_data_pointers_asdict( - self.object_data_pointers(frames) - ), - } - - def frame_intervals(self, frames: t.Dict[int, "Frame"]) -> t.List[FrameInterval]: - """Return frame intervals in which this object is present. - - Parameters - ---------- - frames: dict[int, raillabel.format.Frame] - The dictionary of frames stored under Scene.frames. - - Returns - ------- - list[FrameInterval] - List of the FrameIntervals, where this object is contained. - """ - - frame_uids_containing_object = [ - frame.uid for frame in frames.values() if self._is_object_in_frame(frame) - ] - - return FrameInterval.from_frame_uids(frame_uids_containing_object) - - def object_data_pointers(self, frames: t.Dict[int, "Frame"]) -> t.Dict[str, ElementDataPointer]: - """Create object data pointers used in WebLABEL visualization. - - Parameters - ---------- - frames: dict[int, raillabel.format.Frame] - The dictionary of frames stored under Scene.frames. - - Returns - ------- - dict[str, ElementDataPointer] - ObjectDataPointers dict as required by WebLABEL. Keys are the ObjectDataPointer uids. - """ - - pointer_ids_per_frame = self._collect_pointer_ids_per_frame(frames) - frame_uids_per_pointer_id = self._reverse_frame_pointer_ids(pointer_ids_per_frame) - frame_intervals_per_pointer_id = self._convert_to_intervals(frame_uids_per_pointer_id) - - attributes_per_pointer_id = self._collect_attributes_per_pointer_id(frames) - attribute_pointers_per_pointer_id = self._convert_to_attribute_pointers( - attributes_per_pointer_id - ) - - return self._create_object_data_pointers( - frame_intervals_per_pointer_id, attribute_pointers_per_pointer_id - ) - - # --- Private Methods ------------- - - def _frame_intervals_asdict(self, frame_intervals: t.List[FrameInterval]) -> dict: - return [fi.asdict() for fi in frame_intervals] - - def _object_data_pointers_asdict( - self, object_data_pointers: t.Dict[str, ElementDataPointer] - ) -> dict: - return { - pointer_id: pointer.asdict() for pointer_id, pointer in object_data_pointers.items() - } - - def _is_object_in_frame(self, frame: "Frame") -> bool: - return self.uid in frame.object_data - - def _filtered_annotations(self, frame: "Frame") -> dict_values: - return [ann for ann in frame.annotations.values() if ann.object.uid == self.uid] - - def _collect_pointer_ids_per_frame( - self, frames: t.Dict[int, "Frame"] - ) -> t.Dict[int, t.Set[str]]: - - pointer_ids_per_frame = {} - for frame in frames.values(): - pointer_ids_per_frame[frame.uid] = set() - - for annotation in self._filtered_annotations(frame): - pointer_ids_per_frame[frame.uid].add(annotation.name) - - return pointer_ids_per_frame - - def _reverse_frame_pointer_ids( - self, pointer_ids_per_frame: t.Dict[int, t.Set[str]] - ) -> t.Dict[str, t.Set[int]]: - - frame_uids_per_pointer_id = {} - for frame_uid, pointer_ids in pointer_ids_per_frame.items(): - for pointer_id in pointer_ids: - - if pointer_id not in frame_uids_per_pointer_id: - frame_uids_per_pointer_id[pointer_id] = set() - - frame_uids_per_pointer_id[pointer_id].add(frame_uid) - - return frame_uids_per_pointer_id - - def _convert_to_intervals( - self, frame_uids_per_pointer_id: t.Dict[str, t.Set[int]] - ) -> t.Dict[str, t.List[FrameInterval]]: - - frame_intervals = {} - for pointer_id, frame_uids in frame_uids_per_pointer_id.items(): - frame_intervals[pointer_id] = FrameInterval.from_frame_uids(list(frame_uids)) - - return frame_intervals - - def _collect_attributes_per_pointer_id( - self, frames: t.Dict[int, "Frame"] - ) -> t.Dict[str, t.Dict[str, t.Any]]: - - attributes_per_pointer_id = {} - for frame in frames.values(): - for annotation in self._filtered_annotations(frame): - if annotation.name not in attributes_per_pointer_id: - attributes_per_pointer_id[annotation.name] = {} - - attributes_per_pointer_id[annotation.name].update(annotation.attributes) - - return attributes_per_pointer_id - - def _convert_to_attribute_pointers( - self, attributes_per_pointer_id: t.Dict[str, t.Dict[str, t.Any]] - ) -> t.Dict[str, t.Dict[str, AttributeType]]: - for attributes in attributes_per_pointer_id.values(): - for attribute_name, attribute_value in attributes.items(): - attributes[attribute_name] = AttributeType.from_value(type(attribute_value)) - - return attributes_per_pointer_id - - def _create_object_data_pointers( - self, - frame_intervals_per_pointer_id: t.Dict[str, t.List[FrameInterval]], - attribute_pointers_per_pointer_id: t.Dict[str, t.Dict[str, AttributeType]], - ) -> t.Dict[str, ElementDataPointer]: - - object_data_pointers = {} - for pointer_id in frame_intervals_per_pointer_id: - object_data_pointers[pointer_id] = ElementDataPointer( - uid=pointer_id, - frame_intervals=frame_intervals_per_pointer_id[pointer_id], - attribute_pointers=attribute_pointers_per_pointer_id[pointer_id], - ) - - return object_data_pointers - - # --- Special Methods ------------- - - def __hash__(self) -> int: - """Return hash.""" - return self.uid.__hash__() diff --git a/raillabel/format/raillabel/point2d.py b/raillabel/format/raillabel/point2d.py deleted file mode 100644 index 37d913f..0000000 --- a/raillabel/format/raillabel/point2d.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Point2d: - """A 2d point in an image. - - Parameters - ---------- - x: float or int - The x-coordinate of the point in the image. - y: float or int - The y-coordinate of the point in the image. - """ - - x: float - y: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Point2d": - """Generate a Point2d object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - """ - - return Point2d( - x=data_dict[0], - y=data_dict[1], - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return [float(self.x), float(self.y)] diff --git a/raillabel/format/raillabel/point3d.py b/raillabel/format/raillabel/point3d.py deleted file mode 100644 index fe09775..0000000 --- a/raillabel/format/raillabel/point3d.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Point3d: - """A point in the 3D space. - - Parameters - ---------- - x: float or int - The x-coordinate of the point. - y: float or int - The y-coordinate of the point. - z: float or int - The z-coordinate of the point. - """ - - x: float - y: float - z: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Point3d": - """Generate a Point3d object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - """ - - return Point3d( - x=data_dict[0], - y=data_dict[1], - z=data_dict[2], - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return [float(self.x), float(self.y), float(self.z)] diff --git a/raillabel/format/raillabel/poly2d.py b/raillabel/format/raillabel/poly2d.py deleted file mode 100644 index cfa9e68..0000000 --- a/raillabel/format/raillabel/poly2d.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from ._object_annotation import _ObjectAnnotation -from .object import Object -from .point2d import Point2d - - -@dataclass -class Poly2d(_ObjectAnnotation): - """Sequence of 2D points. Can either be a polygon or polyline. - - Parameters - ---------- - uid: str - This a string representing the unique universal identifier for the annotation. - points: list of raillabel.format.Point2d - List of the 2d points that make up the polyline. - closed: bool - This parameter states, whether the polyline represents a closed shape (a polygon) or an - open line. - mode: str, optional - Mode of the polyline list of values: "MODE_POLY2D_ABSOLUTE" determines that the poly2d list - contains the sequence of (x, y) values of all points of the polyline. "MODE_POLY2D_RELATIVE" - specifies that only the first point of the sequence is defined with its (x, y) values, while - all the rest are defined relative to it. "MODE_POLY2D_SRF6DCC" specifies that SRF6DCC chain - code method is used. "MODE_POLY2D_RS6FCC" specifies that the RS6FCC method is used. Default - is 'MODE_POLY2D_ABSOLUTE'. - object: raillabel.format.Object - A reference to the object, this annotation belongs to. - sensor: raillabel.format.Sensor, optional - A reference to the sensor, this annotation is labeled in. Default is None. - attributes: dict, optional - Attributes of the annotation. Dict keys are the name str of the attribute, values are the - attribute values. Default is {}. - - Properties (read-only) - ---------------------- - name: str - Name of the annotation used by the VCD player for indexing in the object data pointers. - """ - - points: t.List[Point2d] = None - closed: bool = None - mode: str = "MODE_POLY2D_ABSOLUTE" - - OPENLABEL_ID = "poly2d" - _REQ_FIELDS = ["points", "closed"] - - @classmethod - def fromdict(cls, data_dict: dict, sensors: dict, object: Object) -> "Poly2d": - """Generate a Poly2d object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - sensors: dict - Dictionary containing all sensors for the scene. - object: raillabel.format.Object - Object this annotation belongs to. - - Returns - ------- - annotation: Poly2d - Converted annotation. - """ - - return Poly2d( - uid=str(data_dict["uid"]), - closed=data_dict["closed"], - mode=data_dict["mode"], - points=cls._points_fromdict(data_dict), - object=object, - sensor=cls._coordinate_system_fromdict(data_dict, sensors), - attributes=cls._attributes_fromdict(data_dict), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = self._annotation_required_fields_asdict() - - dict_repr["closed"] = bool(self.closed) - dict_repr["val"] = [] - dict_repr["mode"] = self.mode - for point in self.points: - dict_repr["val"].extend(point.asdict()) - - dict_repr.update(self._annotation_optional_fields_asdict()) - - return dict_repr - - @classmethod - def _points_fromdict(cls, data_dict: dict) -> t.List[Point2d]: - points = [] - for i in range(0, len(data_dict["val"]), 2): - points.append(Point2d(x=data_dict["val"][i], y=data_dict["val"][i + 1])) - return points diff --git a/raillabel/format/raillabel/poly3d.py b/raillabel/format/raillabel/poly3d.py deleted file mode 100644 index 28e092b..0000000 --- a/raillabel/format/raillabel/poly3d.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from ._object_annotation import _ObjectAnnotation -from .object import Object -from .point3d import Point3d - - -@dataclass -class Poly3d(_ObjectAnnotation): - """Sequence of 3D points. Can either be a polygon or polyline. - - Parameters - ---------- - uid: str - This a string representing the unique universal identifier for the annotation. - points: list of raillabel.format.Point3d - List of the 3d points that make up the polyline. - closed: bool - This parameter states, whether the polyline represents a closed shape (a polygon) or an - open line. - object: raillabel.format.Object - A reference to the object, this annotation belongs to. - sensor: raillabel.format.Sensor, optional - A reference to the sensor, this annotation is labeled in. Default is None. - attributes: dict, optional - Attributes of the annotation. Dict keys are the name str of the attribute, values are the - attribute values. Default is {}. - - Properties (read-only) - ---------------------- - name: str - Name of the annotation used by the VCD player for indexing in the object data pointers. - """ - - points: t.List[Point3d] = None - closed: bool = None - - OPENLABEL_ID = "poly3d" - _REQ_FIELDS = ["points", "closed"] - - @classmethod - def fromdict(cls, data_dict: dict, sensors: dict, object: Object) -> "Poly3d": - """Generate a Poly3d object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - sensors: dict - Dictionary containing all sensors for the scene. - object: raillabel.format.Object - Object this annotation belongs to. - - Returns - ------- - annotation: Poly3d - Converted annotation. - """ - - return Poly3d( - uid=str(data_dict["uid"]), - closed=data_dict["closed"], - points=cls._points_fromdict(data_dict), - object=object, - sensor=cls._coordinate_system_fromdict(data_dict, sensors), - attributes=cls._attributes_fromdict(data_dict), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = self._annotation_required_fields_asdict() - - dict_repr["closed"] = bool(self.closed) - dict_repr["val"] = [] - for point in self.points: - dict_repr["val"].extend(point.asdict()) - - dict_repr.update(self._annotation_optional_fields_asdict()) - - return dict_repr - - @classmethod - def _points_fromdict(cls, data_dict: dict) -> t.List[Point3d]: - points = [] - for i in range(0, len(data_dict["val"]), 3): - points.append( - Point3d(x=data_dict["val"][i], y=data_dict["val"][i + 1], z=data_dict["val"][i + 2]) - ) - return points diff --git a/raillabel/format/raillabel/quaternion.py b/raillabel/format/raillabel/quaternion.py deleted file mode 100644 index 2e77405..0000000 --- a/raillabel/format/raillabel/quaternion.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Quaternion: - """A quaternion. - - Parameters - ---------- - x: float or int - The x component of the quaternion. - y: float or int - The y component of the quaternion. - z: float or int - The z component of the quaternion. - w: float or int - The w component of the quaternion. - """ - - x: float - y: float - z: float - w: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Quaternion": - """Generate a Quaternion object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - """ - - return Quaternion( - x=data_dict[0], - y=data_dict[1], - z=data_dict[2], - w=data_dict[3], - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return [float(self.x), float(self.y), float(self.z), float(self.w)] diff --git a/raillabel/format/raillabel/scene.py b/raillabel/format/raillabel/scene.py deleted file mode 100644 index d3c7fcb..0000000 --- a/raillabel/format/raillabel/scene.py +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass, field - -from ... import exceptions -from ..._util._clean_dict import _clean_dict -from ..._util._warning import _warning -from .frame import Frame -from .frame_interval import FrameInterval -from .metadata import Metadata -from .object import Object -from .sensor import Sensor - - -@dataclass -class Scene: - """The root RailLabel class, which contains all data. - - Parameters - ---------- - metadata: raillabel.format.Metadata - This object contains information, that is, metadata, about the annotation file itself. - sensors: dict of raillabel.format.Sensor, optional - Dictionary of raillabel.format.Sensors. Dictionary keys are the sensor uids. Default is {}. - objects: dict of raillabel.format.Object, optional - Dictionary of raillabel.format.Objects. Dictionary keys are the object uids. Default is {}. - frames: dict of raillabel.format.Frame, optional - Dict of frames in the scene. Dictionary keys are the frame uids. Default is {}. - - Properties (read-only) - ---------------------- - frame_intervals: list[FrameIntervals] - List of frame intervals describing the frames present in this scene. - """ - - metadata: Metadata - sensors: t.Dict[str, Sensor] = field(default_factory=dict) - objects: t.Dict[str, Object] = field(default_factory=dict) - frames: t.Dict[int, Frame] = field(default_factory=dict) - - @property - def frame_intervals(self) -> t.List[FrameInterval]: - """Return frame intervals of the present frames.""" - return FrameInterval.from_frame_uids(list(self.frames.keys())) - - # === Public Methods ========================================================================== - - @classmethod - def fromdict(cls, data_dict: dict, subschema_version: t.Optional[str] = None) -> "Scene": - """Generate a Scene object from a RailLABEL-dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - subschema_version: str, optional - Version of the RailLabel subschema. - - Returns - ------- - raillabel.format.Scene - Converted Scene object. - - Raises - ------ - raillabel.exceptions.MissingCoordinateSystemError - if a stream has no corresponding coordinate system. - raillabel.exceptions.MissingStreamError - if a coordinate system has no corresponding stream. - raillabel.exceptions.UnsupportedParentError - if a coordinate system has no corresponding stream. - """ - - data_dict = cls._prepare_data(data_dict) - - sensors = cls._sensors_fromdict(data_dict["streams"], data_dict["coordinate_systems"]) - objects = cls._objects_fromdict(data_dict["objects"]) - - return Scene( - metadata=Metadata.fromdict(data_dict["metadata"], subschema_version), - sensors=sensors, - objects=objects, - frames=cls._frames_fromdict(data_dict["frames"], sensors, objects), - ) - - def asdict(self, calculate_pointers: bool = True) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this Scene. - calculate_pointers: bool, optional - If True, object_data_pointers and Object frame_intervals will be calculated. Default - is True. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return { - "openlabel": _clean_dict( - { - "metadata": self.metadata.asdict(), - "streams": self._streams_asdict(self.sensors), - "coordinate_systems": self._coordinate_systems_asdict(self.sensors), - "objects": self._objects_asdict(self.objects, calculate_pointers), - "frames": self._frames_asdict(self.frames), - "frame_intervals": self._frame_intervals_asdict(self.frame_intervals), - } - ) - } - - # === Private Methods ========================================================================= - - # --- fromdict() ---------------------------- - - @classmethod - def _prepare_data(cls, data: dict) -> dict: - """Add optional fields to dict to simplify interaction. - - Parameters - ---------- - data : dict - JSON data. - - Returns - ------- - dict - Enhanced JSON data. - """ - - if "coordinate_systems" not in data["openlabel"]: - data["openlabel"]["coordinate_systems"] = {} - - if "streams" not in data["openlabel"]: - data["openlabel"]["streams"] = {} - - if "objects" not in data["openlabel"]: - data["openlabel"]["objects"] = {} - - if "frames" not in data["openlabel"]: - data["openlabel"]["frames"] = {} - - return data["openlabel"] - - @classmethod - def _sensors_fromdict( - cls, streams_dict: dict, coordinate_systems_dict: dict - ) -> t.Dict[str, Sensor]: - - cls._check_sensor_completeness(streams_dict, coordinate_systems_dict) - - sensors = {} - - for stream_id in streams_dict: - sensors[stream_id] = Sensor.fromdict( - uid=stream_id, - cs_data_dict=coordinate_systems_dict[stream_id], - stream_data_dict=streams_dict[stream_id], - ) - - return sensors - - @classmethod - def _check_sensor_completeness(cls, streams_dict: dict, coordinate_systems_dict: dict): - - for stream_uid in streams_dict: - if stream_uid not in coordinate_systems_dict: - raise exceptions.MissingCoordinateSystemError( - f"Stream {stream_uid} has no corresponding coordinate system." - ) - - for cs_uid in coordinate_systems_dict: - if cs_uid == "base": - continue - - if coordinate_systems_dict[cs_uid]["parent"] != "base": - raise exceptions.UnsupportedParentError( - f"Only 'base' is permitted as a parent for coordinate system {cs_uid}, " - + f"not {coordinate_systems_dict[cs_uid]['parent']}." - ) - - if cs_uid not in streams_dict: - raise exceptions.MissingStreamError( - f"Coordinate sytem {cs_uid} has no corresponding stream." - ) - - @classmethod - def _objects_fromdict(cls, object_dict: dict) -> t.Dict[str, Object]: - return {uid: Object.fromdict(object, uid) for uid, object in object_dict.items()} - - @classmethod - def _frames_fromdict( - cls, frames_dict: dict, sensors: t.Dict[str, Sensor], objects: t.Dict[str, Object] - ) -> t.Dict[int, Frame]: - - frames = {} - for frame_uid, frame_dict in frames_dict.items(): - frame_uid = int(frame_uid) - - if frame_uid in frames: - _warning( - f"Frame UID {frame_uid} is contained more than once in the scene. " - + "The duplicate frame will be omitted." - ) - continue - - frames[frame_uid] = Frame.fromdict(frame_uid, frame_dict, objects, sensors) - - return frames - - # --- asdict() ------------------------------ - - def _streams_asdict(self, sensors: t.Dict[str, Sensor]) -> dict: - return {uid: sensor.asdict()["stream"] for uid, sensor in sensors.items()} - - def _coordinate_systems_asdict(self, sensors: t.Dict[str, Sensor]) -> dict: - - if len(sensors) == 0: - return None - - coordinate_systems = {"base": {"type": "local", "parent": "", "children": []}} - - for uid, sensor in sensors.items(): - coordinate_systems[uid] = sensor.asdict()["coordinate_system"] - coordinate_systems["base"]["children"].append(uid) - - return coordinate_systems - - def _objects_asdict(self, objects: t.Dict[str, Object], calculate_pointers: bool) -> dict: - - if calculate_pointers: - return {str(uid): object.asdict(self.frames) for uid, object in objects.items()} - else: - return {str(uid): object.asdict() for uid, object in objects.items()} - - def _frames_asdict(self, frames: t.Dict[int, Frame]) -> dict: - return {str(uid): frame.asdict() for uid, frame in frames.items()} - - def _frame_intervals_asdict(self, frame_intervals: t.List[FrameInterval]) -> t.List[dict]: - return [fi.asdict() for fi in frame_intervals] diff --git a/raillabel/format/raillabel/seg3d.py b/raillabel/format/raillabel/seg3d.py deleted file mode 100644 index 948295d..0000000 --- a/raillabel/format/raillabel/seg3d.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from ._object_annotation import _ObjectAnnotation -from .object import Object - - -@dataclass -class Seg3d(_ObjectAnnotation): - """The 3D segmentation of a lidar pointcloud. - - Parameters - ---------- - uid: str - This a string representing the unique universal identifier of the annotation. - point_ids: list of int - The list of point indices. - object: raillabel.format.Object - A reference to the object, this annotation belongs to. - sensor: raillabel.format.Sensor, optional - A reference to the sensor, this annotation is labeled in. Default is None. - attributes: dict, optional - Attributes of the annotation. Dict keys are the name str of the attribute, values are the - attribute values. Default is {}. - - Properties (read-only) - ---------------------- - name: str - Name of the annotation used by the VCD player for indexing in the object data pointers. - """ - - point_ids: t.List[int] = None - - OPENLABEL_ID = "vec" - _REQ_FIELDS = ["point_ids"] - - @classmethod - def fromdict(cls, data_dict: dict, sensors: dict, object: Object) -> "Seg3d": - """Generate a Seg3d object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - sensors: dict - Dictionary containing all sensors for the scene. - object: raillabel.format.Object - Object this annotation belongs to. - - Returns - ------- - annotation: Seg3d - Converted annotation. - """ - - return Seg3d( - uid=str(data_dict["uid"]), - point_ids=data_dict["val"], - object=object, - sensor=cls._coordinate_system_fromdict(data_dict, sensors), - attributes=cls._attributes_fromdict(data_dict), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = self._annotation_required_fields_asdict() - - dict_repr["val"] = [int(pid) for pid in self.point_ids] - - dict_repr.update(self._annotation_optional_fields_asdict()) - - return dict_repr diff --git a/raillabel/format/raillabel/sensor.py b/raillabel/format/raillabel/sensor.py deleted file mode 100644 index 94f2658..0000000 --- a/raillabel/format/raillabel/sensor.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from enum import Enum - -from .intrinsics_pinhole import IntrinsicsPinhole -from .intrinsics_radar import IntrinsicsRadar -from .point3d import Point3d -from .quaternion import Quaternion -from .transform import Transform - - -@dataclass -class Sensor: - """A reference to a physical sensor on the train. - - A sensor in the devkit corresponds to one coordinate_system and one stream in the data format. - This distinction is set by the OpenLABEL standard, but is not relevant for our data. - Therefore, we decided to combine these fields. - - Parameters - ---------- - uid: str - This is the friendly name of the sensor as well as its identifier. Must be - unique. - extrinsics: raillabel.format.Transform, optional - The external calibration of the sensor defined by the 3D transform to the coordinate - system origin. Default is None. - intrinsics: raillabel.format.IntrinsicsPinhole or raillabel.format.IntrinsicsRadar, optional - The intrinsic calibration of the sensor. Default is None. - type: raillabel.format.SensorType, optional - Information about the kind of sensor. Default is None. - uri: str, optional - Name of the subdirectory containing the sensor files. Default is None. - description: str, optional - Description of the sensor. Default is None. - """ - - uid: str - extrinsics: t.Optional[Transform] = None - intrinsics: t.Optional[t.Union[IntrinsicsPinhole, IntrinsicsRadar]] = None - type: t.Optional["SensorType"] = None - uri: t.Optional[str] = None - description: t.Optional[str] = None - - @classmethod - def fromdict(cls, uid: str, cs_data_dict: dict, stream_data_dict: dict) -> "Sensor": - """Generate a Sensor object from a dict. - - Parameters - ---------- - uid: str - Unique identifier of the sensor. - cs_data_dict: dict - RailLabel format dict containing the data about the coordinate system. - stream_data_dict: dict - RailLabel format dict containing the data about the stream. - - Returns - ------- - sensor: raillabel.format.Sensor - Converted Sensor object. - """ - - return Sensor( - uid=uid, - extrinsics=cls._extrinsics_fromdict(cs_data_dict), - intrinsics=cls._intrinsics_fromdict( - stream_data_dict, cls._type_fromdict(stream_data_dict) - ), - type=cls._type_fromdict(stream_data_dict), - uri=stream_data_dict.get("uri"), - description=stream_data_dict.get("description"), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the RailLabel schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - """ - - return { - "coordinate_system": self._as_coordinate_system_dict(), - "stream": self._as_stream_dict(), - } - - def _as_coordinate_system_dict(self) -> dict: - - coordinate_system_repr = {"type": "sensor", "parent": "base"} - - if self.extrinsics is not None: - coordinate_system_repr["pose_wrt_parent"] = self.extrinsics.asdict() - - return coordinate_system_repr - - def _as_stream_dict(self) -> dict: - - stream_repr = {} - - if self.type is not None: - stream_repr["type"] = str(self.type.value) - - if self.uri is not None: - stream_repr["uri"] = str(self.uri) - - if self.description is not None: - stream_repr["description"] = str(self.description) - - if isinstance(self.intrinsics, IntrinsicsPinhole): - stream_repr["stream_properties"] = {"intrinsics_pinhole": self.intrinsics.asdict()} - - elif isinstance(self.intrinsics, IntrinsicsRadar): - stream_repr["stream_properties"] = {"intrinsics_radar": self.intrinsics.asdict()} - - return stream_repr - - @classmethod - def _extrinsics_fromdict(cls, data_dict) -> t.Optional[Transform]: - - if "pose_wrt_parent" not in data_dict: - return None - - return Transform( - pos=Point3d( - x=data_dict["pose_wrt_parent"]["translation"][0], - y=data_dict["pose_wrt_parent"]["translation"][1], - z=data_dict["pose_wrt_parent"]["translation"][2], - ), - quat=Quaternion( - x=data_dict["pose_wrt_parent"]["quaternion"][0], - y=data_dict["pose_wrt_parent"]["quaternion"][1], - z=data_dict["pose_wrt_parent"]["quaternion"][2], - w=data_dict["pose_wrt_parent"]["quaternion"][3], - ), - ) - - @classmethod - def _intrinsics_fromdict( - cls, data_dict, sensor_type: t.Optional["SensorType"] - ) -> t.Optional[IntrinsicsPinhole]: - - if "stream_properties" not in data_dict: - return None - - if sensor_type == SensorType.CAMERA: - - if "intrinsics_pinhole" in data_dict["stream_properties"]: - return IntrinsicsPinhole.fromdict( - data_dict["stream_properties"]["intrinsics_pinhole"] - ) - - elif sensor_type == SensorType.RADAR: - - if "intrinsics_radar" in data_dict["stream_properties"]: - return IntrinsicsRadar.fromdict(data_dict["stream_properties"]["intrinsics_radar"]) - - return None - - @classmethod - def _type_fromdict(cls, data_dict) -> t.Optional["SensorType"]: - - if "type" not in data_dict: - return None - - return SensorType(data_dict["type"]) - - -class SensorType(Enum): - """Enumeration representing all possible sensor types.""" - - CAMERA = "camera" - LIDAR = "lidar" - RADAR = "radar" - GPS_IMU = "gps_imu" - OTHER = "other" diff --git a/raillabel/format/raillabel/sensor_reference.py b/raillabel/format/raillabel/sensor_reference.py deleted file mode 100644 index 6f7ce70..0000000 --- a/raillabel/format/raillabel/sensor_reference.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import decimal -import typing as t -from dataclasses import dataclass - -from ..._util._warning import _warning -from .sensor import Sensor - - -@dataclass -class SensorReference: - """A reference to a sensor in a specific frame. - - Parameters - ---------- - sensor: raillabel.format.Sensor - The sensor this SensorReference corresponds to. - timestamp: decimal.Decimal - Timestamp containing the Unix epoch time of the sensor in a specific frame with up to - nanosecond precision. - uri: str, optional - URI to the file corresponding to the frame recording in the particular frame. Default is - None. - """ - - sensor: Sensor - timestamp: decimal.Decimal - uri: t.Optional[str] = None - - @classmethod - def fromdict(cls, data_dict: dict, sensor: Sensor) -> "SensorReference": - """Generate a SensorReference object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - sensor: raillabel.format.Sensor - Sensor corresponding to this SensorReference. - - Returns - ------- - sensor_reference: raillabel.format.SensorReference - Converted SensorReference object. - """ - - return SensorReference( - sensor=sensor, - timestamp=cls._timestamp_fromdict(data_dict["stream_properties"]), - uri=data_dict.get("uri"), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = {"stream_properties": {"sync": {"timestamp": str(self.timestamp)}}} - - if self.uri is not None: - dict_repr["uri"] = self.uri - - return dict_repr - - @classmethod - def _timestamp_fromdict(cls, data_dict: dict) -> decimal.Decimal: - - if "stream_sync" in data_dict: - _warning( - "Deprecated field 'stream_sync' identified. " - + "Please update file with raillabel.save()." - ) - return decimal.Decimal(data_dict["stream_sync"]["timestamp"]) - - return decimal.Decimal(data_dict["sync"]["timestamp"]) diff --git a/raillabel/format/raillabel/size2d.py b/raillabel/format/raillabel/size2d.py deleted file mode 100644 index 9976f70..0000000 --- a/raillabel/format/raillabel/size2d.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Size2d: - """The size of a rectangle in a 2d image. - - Parameters - ---------- - x: float or int - The size along the x-axis. - y: float or int - The size along the y-axis. - """ - - x: float - y: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Size2d": - """Generate a Size2d object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - """ - - return Size2d( - x=data_dict[0], - y=data_dict[1], - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return [float(self.x), float(self.y)] diff --git a/raillabel/format/raillabel/size3d.py b/raillabel/format/raillabel/size3d.py deleted file mode 100644 index 61d926d..0000000 --- a/raillabel/format/raillabel/size3d.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Size3d: - """The 3D size of a cube. - - Parameters - ---------- - x: float or int - The size along the x-axis. - y: float or int - The size along the y-axis. - z: float or int - The size along the z-axis. - """ - - x: float - y: float - z: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Size3d": - """Generate a Size3d object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - """ - - return Size3d( - x=data_dict[0], - y=data_dict[1], - z=data_dict[2], - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - return [float(self.x), float(self.y), float(self.z)] diff --git a/raillabel/format/raillabel/transform.py b/raillabel/format/raillabel/transform.py deleted file mode 100644 index bffddce..0000000 --- a/raillabel/format/raillabel/transform.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - -from .point3d import Point3d -from .quaternion import Quaternion - - -@dataclass -class Transform: - """A transformation between two coordinate systems. - - Parameters - ---------- - pos: raillabel.format.Point3d - Translation with regards to the parent coordinate system. - quat: raillabel.format.Quaternion - Rotation quaternion with regards to the parent coordinate system. - """ - - pos: Point3d - quat: Quaternion - - @classmethod - def fromdict(cls, data_dict: dict) -> "Transform": - """Generate a Transform object from a dict. - - Parameters - ---------- - data_dict: dict - RailLabel format snippet containing the relevant data. - """ - - return Transform( - pos=Point3d.fromdict(data_dict["translation"]), - quat=Quaternion.fromdict(data_dict["quaternion"]), - ) - - def asdict(self) -> dict: - """Export self as a dict compatible with the OpenLABEL schema. - - Returns - ------- - dict_repr: dict - Dict representation of this class instance. - - Raises - ------ - ValueError - if an attribute can not be converted to the type required by the OpenLabel schema. - """ - - dict_repr = { - "translation": self.pos.asdict(), - "quaternion": self.quat.asdict(), - } - - return dict_repr diff --git a/raillabel/format/scene.py b/raillabel/format/scene.py new file mode 100644 index 0000000..88fae19 --- /dev/null +++ b/raillabel/format/scene.py @@ -0,0 +1,238 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from copy import deepcopy +from dataclasses import dataclass, field +from uuid import UUID + +from raillabel.filter._filter_abc import _AnnotationLevelFilter, _FilterAbc, _FrameLevelFilter +from raillabel.json_format import ( + JSONCoordinateSystem, + JSONFrame, + JSONObject, + JSONScene, + JSONSceneContent, + JSONStreamCamera, + JSONStreamOther, + JSONStreamRadar, +) + +from .bbox import Bbox +from .camera import Camera +from .cuboid import Cuboid +from .frame import Frame +from .frame_interval import FrameInterval +from .gps_imu import GpsImu +from .lidar import Lidar +from .metadata import Metadata +from .object import Object +from .other_sensor import OtherSensor +from .poly2d import Poly2d +from .poly3d import Poly3d +from .radar import Radar +from .seg3d import Seg3d + + +@dataclass +class Scene: + """The root RailLabel class, which contains all data.""" + + metadata: Metadata + "Container of information about the annotation file itself." + + sensors: dict[str, Camera | Lidar | Radar | GpsImu | OtherSensor] = field(default_factory=dict) + "The sensors used in this scene. Keys are sensor names." + + objects: dict[UUID, Object] = field(default_factory=dict) + "Unique objects (like a specific person) in this scene. Keys are object uuids" + + frames: dict[int, Frame] = field(default_factory=dict) + "A container of dynamic, timewise, information. Keys are the frame integer number." + + @classmethod + def from_json(cls, json: JSONScene) -> Scene: + """Construct a scene from a json object.""" + return Scene( + metadata=Metadata.from_json(json.openlabel.metadata), + sensors=_sensors_from_json(json.openlabel.streams, json.openlabel.coordinate_systems), + objects=_objects_from_json(json.openlabel.objects), + frames=_frames_from_json(json.openlabel.frames), + ) + + def to_json(self) -> JSONScene: + """Export this scene into the RailLabel JSON format.""" + return JSONScene( + openlabel=JSONSceneContent( + metadata=self.metadata.to_json(), + streams={ + sensor_id: sensor.to_json()[0] for sensor_id, sensor in self.sensors.items() + }, + coordinate_systems=_coordinate_systems_to_json(self.sensors), + objects={ + obj_id: obj.to_json(obj_id, self.frames) for obj_id, obj in self.objects.items() + }, + frames={ + frame_id: frame.to_json(self.objects) for frame_id, frame in self.frames.items() + }, + frame_intervals=[ + fi.to_json() for fi in FrameInterval.from_frame_ids(list(self.frames.keys())) + ], + ) + ) + + def filter(self, filters: list[_FilterAbc]) -> Scene: + """Return a scene with annotations, sensors, objects and frames excluded.""" + frame_filters, annotation_filters = _separate_filters(filters) + + filtered_scene = Scene(metadata=deepcopy(self.metadata)) + filtered_scene.frames = _filter_frames(self, frame_filters, annotation_filters) + filtered_scene.sensors = _get_used_sensors(self, filtered_scene) + filtered_scene.objects = _get_used_objects(self, filtered_scene) + + return filtered_scene + + +def _sensors_from_json( + json_streams: dict[str, JSONStreamCamera | JSONStreamOther | JSONStreamRadar] | None, + json_coordinate_systems: dict[str, JSONCoordinateSystem] | None, +) -> dict[str, Camera | Lidar | Radar | GpsImu | OtherSensor]: + sensors: dict[str, Camera | Lidar | Radar | GpsImu | OtherSensor] = {} + + if json_streams is None or json_coordinate_systems is None: + return sensors + + for sensor_id, json_stream in json_streams.items(): + json_coordinate_system = json_coordinate_systems[sensor_id] + + if isinstance(json_stream, JSONStreamCamera): + sensors[sensor_id] = Camera.from_json(json_stream, json_coordinate_system) + + if isinstance(json_stream, JSONStreamRadar): + sensors[sensor_id] = Radar.from_json(json_stream, json_coordinate_system) + + if isinstance(json_stream, JSONStreamOther) and json_stream.type == "lidar": + sensors[sensor_id] = Lidar.from_json(json_stream, json_coordinate_system) + + if isinstance(json_stream, JSONStreamOther) and json_stream.type == "gps_imu": + sensors[sensor_id] = GpsImu.from_json(json_stream, json_coordinate_system) + + if isinstance(json_stream, JSONStreamOther) and json_stream.type == "other": + sensors[sensor_id] = OtherSensor.from_json(json_stream, json_coordinate_system) + + return sensors + + +def _objects_from_json(json_objects: dict[UUID, JSONObject] | None) -> dict[UUID, Object]: + if json_objects is None: + return {} + + return {obj_id: Object.from_json(json_obj) for obj_id, json_obj in json_objects.items()} + + +def _frames_from_json(json_frames: dict[int, JSONFrame] | None) -> dict[int, Frame]: + if json_frames is None: + return {} + + return {frame_id: Frame.from_json(json_frame) for frame_id, json_frame in json_frames.items()} + + +def _coordinate_systems_to_json( + sensors: dict[str, Camera | Lidar | Radar | GpsImu | OtherSensor], +) -> dict[str, JSONCoordinateSystem]: + json_coordinate_systems = { + sensor_id: sensor.to_json()[1] for sensor_id, sensor in sensors.items() + } + json_coordinate_systems["base"] = JSONCoordinateSystem( + parent="", + type="local", + pose_wrt_parent=None, + children=list(json_coordinate_systems.keys()), + ) + return json_coordinate_systems + + +def _separate_filters( + all_filters: list[_FilterAbc], +) -> tuple[list[_FrameLevelFilter], list[_AnnotationLevelFilter]]: + frame_filters = [] + annotation_filters = [] + for filter_ in all_filters: + if isinstance(filter_, _FrameLevelFilter): + frame_filters.append(filter_) + + if isinstance(filter_, _AnnotationLevelFilter): + annotation_filters.append(filter_) + + return frame_filters, annotation_filters + + +def _filter_frames( + scene: Scene, + frame_filters: list[_FrameLevelFilter], + annotation_filters: list[_AnnotationLevelFilter], +) -> dict[int, Frame]: + filtered_frames = {} + + for frame_id, frame in scene.frames.items(): + if _frame_passes_all_filters(frame_id, frame, frame_filters): + filtered_frames[frame_id] = Frame( + timestamp=deepcopy(frame.timestamp), + sensors=deepcopy(frame.sensors), + frame_data=deepcopy(frame.frame_data), + annotations=_filter_annotations(frame, annotation_filters, scene), + ) + + return filtered_frames + + +def _filter_annotations( + frame: Frame, annotation_filters: list[_AnnotationLevelFilter], scene: Scene +) -> dict[UUID, Bbox | Cuboid | Poly2d | Poly3d | Seg3d]: + annotations = {} + + for annotation_id, annotation in frame.annotations.items(): + if _annotation_passes_all_filters(annotation_id, annotation, annotation_filters, scene): + annotations[annotation_id] = deepcopy(annotation) + + return annotations + + +def _frame_passes_all_filters( + frame_id: int, frame: Frame, frame_filters: list[_FrameLevelFilter] +) -> bool: + return all(filter_.passes_filter(frame_id, frame) for filter_ in frame_filters) + + +def _annotation_passes_all_filters( + annotation_id: UUID, + annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, + annotation_filters: list[_AnnotationLevelFilter], + scene: Scene, +) -> bool: + return all( + filter_.passes_filter(annotation_id, annotation, scene) for filter_ in annotation_filters + ) + + +def _get_used_sensors( + scene: Scene, filtered_scene: Scene +) -> dict[str, Camera | Lidar | Radar | GpsImu | OtherSensor]: + used_sensors = {} + for frame in filtered_scene.frames.values(): + for annotation in frame.annotations.values(): + if annotation.sensor_id not in used_sensors: + used_sensors[annotation.sensor_id] = deepcopy(scene.sensors[annotation.sensor_id]) + + return used_sensors + + +def _get_used_objects(scene: Scene, filtered_scene: Scene) -> dict[UUID, Object]: + used_objects = {} + for frame in filtered_scene.frames.values(): + for annotation in frame.annotations.values(): + if annotation.object_id not in used_objects: + used_objects[annotation.object_id] = deepcopy(scene.objects[annotation.object_id]) + + return used_objects diff --git a/raillabel/format/seg3d.py b/raillabel/format/seg3d.py new file mode 100644 index 0000000..31dd7c0 --- /dev/null +++ b/raillabel/format/seg3d.py @@ -0,0 +1,52 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import UUID + +from raillabel.json_format import JSONVec + +from ._attributes import _attributes_from_json, _attributes_to_json + + +@dataclass +class Seg3d: + """The 3D segmentation of a lidar pointcloud.""" + + point_ids: list[int] + "The list of point indices." + + object_id: UUID + "The unique identifyer of the real-life object, this annotation belongs to." + + sensor_id: str + "The unique identifyer of the sensor this annotation is labeled in." + + attributes: dict[str, float | bool | str | list] + "Additional information associated with the annotation." + + @classmethod + def from_json(cls, json: JSONVec, object_id: UUID) -> Seg3d: + """Construct an instant of this class from RailLabel JSON data.""" + return Seg3d( + point_ids=[int(point_id) for point_id in json.val], + object_id=object_id, + sensor_id=json.coordinate_system, + attributes=_attributes_from_json(json.attributes), + ) + + def to_json(self, uid: UUID, object_type: str) -> JSONVec: + """Export this object into the RailLabel JSON format.""" + return JSONVec( + name=self.name(object_type), + val=self.point_ids, + coordinate_system=self.sensor_id, + uid=uid, + attributes=_attributes_to_json(self.attributes), + ) + + def name(self, object_type: str) -> str: + """Return the name of the annotation used for indexing in the object data pointers.""" + return f"{self.sensor_id}__vec__{object_type}" diff --git a/raillabel/format/sensor_reference.py b/raillabel/format/sensor_reference.py new file mode 100644 index 0000000..3a547c8 --- /dev/null +++ b/raillabel/format/sensor_reference.py @@ -0,0 +1,38 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass +from decimal import Decimal + +from raillabel.json_format import JSONStreamSync, JSONStreamSyncProperties, JSONStreamSyncTimestamp + + +@dataclass +class SensorReference: + """A reference to a sensor in a specific frame.""" + + timestamp: Decimal + """Timestamp containing the Unix epoch time of the sensor in a specific frame with up to + nanosecond precision.""" + + uri: str | None = None + "URI to the file corresponding to the frame recording in the particular frame." + + @classmethod + def from_json(cls, json: JSONStreamSync) -> SensorReference: + """Construct an instant of this class from RailLabel JSON data.""" + return SensorReference( + timestamp=Decimal(json.stream_properties.sync.timestamp), + uri=json.uri, + ) + + def to_json(self) -> JSONStreamSync: + """Export this object into the RailLabel JSON format.""" + return JSONStreamSync( + stream_properties=JSONStreamSyncProperties( + sync=JSONStreamSyncTimestamp(timestamp=str(self.timestamp)), + ), + uri=self.uri, + ) diff --git a/raillabel/format/size2d.py b/raillabel/format/size2d.py new file mode 100644 index 0000000..5afe2a6 --- /dev/null +++ b/raillabel/format/size2d.py @@ -0,0 +1,26 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Size2d: + """The size of a rectangle in a 2d image.""" + + x: float + "The size along the x-axis." + + y: float + "The size along the y-axis." + + @classmethod + def from_json(cls, json: tuple[float, float]) -> Size2d: + """Construct an instant of this class from RailLabel JSON data.""" + return Size2d(x=json[0], y=json[1]) + + def to_json(self) -> tuple[float, float]: + """Export this object into the RailLabel JSON format.""" + return (self.x, self.y) diff --git a/raillabel/format/size3d.py b/raillabel/format/size3d.py new file mode 100644 index 0000000..7810689 --- /dev/null +++ b/raillabel/format/size3d.py @@ -0,0 +1,29 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class Size3d: + """The 3D size of a cube.""" + + x: float + "The size along the x-axis." + + y: float + "The size along the y-axis." + + z: float + "The size along the z-axis." + + @classmethod + def from_json(cls, json: tuple[float, float, float]) -> Size3d: + """Construct an instant of this class from RailLabel JSON data.""" + return Size3d(x=json[0], y=json[1], z=json[2]) + + def to_json(self) -> tuple[float, float, float]: + """Export this object into the RailLabel JSON format.""" + return (self.x, self.y, self.z) diff --git a/raillabel/format/transform.py b/raillabel/format/transform.py new file mode 100644 index 0000000..defe708 --- /dev/null +++ b/raillabel/format/transform.py @@ -0,0 +1,37 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from dataclasses import dataclass + +from raillabel.json_format import JSONTransformData + +from .point3d import Point3d +from .quaternion import Quaternion + + +@dataclass +class Transform: + """A transformation between two coordinate systems.""" + + pos: Point3d + "Translation with regards to the parent coordinate system." + + quat: Quaternion + "Rotation quaternion with regards to the parent coordinate system." + + @classmethod + def from_json(cls, json: JSONTransformData) -> Transform: + """Construct an instant of this class from RailLabel JSON data.""" + return Transform( + pos=Point3d.from_json(json.translation), + quat=Quaternion.from_json(json.quaternion), + ) + + def to_json(self) -> JSONTransformData: + """Export this object into the RailLabel JSON format.""" + return JSONTransformData( + translation=self.pos.to_json(), + quaternion=self.quat.to_json(), + ) diff --git a/raillabel/format/understand_ai/__init__.py b/raillabel/format/understand_ai/__init__.py deleted file mode 100644 index 52adc36..0000000 --- a/raillabel/format/understand_ai/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 -"""Module containing all relevant understand.ai format classes.""" - -from .bounding_box_2d import BoundingBox2d -from .bounding_box_3d import BoundingBox3d -from .coordinate_system import CoordinateSystem -from .frame import Frame -from .metadata import Metadata -from .point_3d import Point3d -from .polygon_2d import Polygon2d -from .polyline_2d import Polyline2d -from .quaternion import Quaternion -from .scene import Scene -from .segmentation_3d import Segmentation3d -from .sensor_reference import SensorReference -from .size_3d import Size3d diff --git a/raillabel/format/understand_ai/_annotation.py b/raillabel/format/understand_ai/_annotation.py deleted file mode 100644 index 257043f..0000000 --- a/raillabel/format/understand_ai/_annotation.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from abc import ABC, abstractmethod, abstractproperty -from dataclasses import dataclass -from uuid import UUID - -from ..._util._attribute_type import AttributeType -from ._translation import translate_class_id, translate_sensor_id -from .sensor_reference import SensorReference - - -@dataclass -class _Annotation(ABC): - - id: UUID - object_id: UUID - class_name: str - attributes: dict - sensor: SensorReference - - @property - @abstractproperty - def OPENLABEL_ID(self) -> t.List[str]: - raise NotImplementedError - - @classmethod - @abstractmethod - def fromdict(cls, data_dict: t.Dict) -> t.Type["_Annotation"]: - raise NotImplementedError - - def to_raillabel(self) -> t.Tuple[dict, str, str, dict]: - """Convert to a raillabel compatible dict. - - Returns - ------- - annotation: dict - Dictionary valid for the raillabel schema. - object_id: str - Friendly identifier of the object this annotation belongs to. - class_name: str - Friendly identifier of the class the annotated object belongs to. - sensor_reference: dict - Dictionary of the sensor reference. - """ - - return ( - { - "name": str(self.id), - "val": self._val_to_raillabel(), - "coordinate_system": translate_sensor_id(self.sensor.type), - "attributes": self._attributes_to_raillabel(), - }, - str(self.object_id), - translate_class_id(self.class_name), - self.sensor.to_raillabel()[1], - ) - - def _attributes_to_raillabel(self) -> dict: - - attributes = {} - - for attr_name, attr_value in self.attributes.items(): - - attr_type = AttributeType.from_value(type(attr_value)).value - - if attr_type not in attributes: - attributes[attr_type] = [] - - attributes[attr_type].append({"name": attr_name, "val": attr_value}) - - return attributes diff --git a/raillabel/format/understand_ai/_translation.py b/raillabel/format/understand_ai/_translation.py deleted file mode 100644 index 79b56fb..0000000 --- a/raillabel/format/understand_ai/_translation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import json -from pathlib import Path - - -def translate_sensor_id(original_sensor_id: str) -> str: - """Translate deprecated sensor ids to the correct ones. - - Parameters - ---------- - original_sensor_id : str - Original id of the sensor. - - Returns - ------- - str - Translated id or original_sensor_id, if no translation could be found. - """ - return TRANSLATION["streams"].get(original_sensor_id, original_sensor_id) - - -def translate_class_id(original_class_id: str) -> str: - """Translate deprecated class ids to the correct ones. - - Parameters - ---------- - original_class_id : str - Original id of the class. - - Returns - ------- - str - Translated id or original_class_id, if no translation could be found. - """ - return TRANSLATION["classes"].get(original_class_id, original_class_id) - - -def fetch_sensor_type(sensor_id: str) -> str: - """Fetch sensor type from translation file. - - Parameters - ---------- - sensor_id : str - Id of the sensor. - - Returns - ------- - str - Sensor type or 'other' if sensor_id not found in translation.json. - """ - return TRANSLATION["stream_types"].get(sensor_id, "other") - - -def fetch_sensor_resolutions(sensor_id: str) -> dict: - """Fetch sensor resolution from translation file. - - Parameters - ---------- - sensor_id : str - Id of the sensor. - - Returns - ------- - dict - Dictionary containing the resolution information. Key 'x' contains the width in pixels, - key 'y' contains the height in pixels. If the sensor is a radar, 'resolution_px_per_m' is - also included. - """ - return TRANSLATION["stream_resolutions"].get( - sensor_id, {"x": None, "y": None, "resolution_px_per_m": None} - ) - - -def _load_translation(): - """Load the translation file when the module is imported. - - This prevents it from beeing loaded for every annotation. - """ - - global TRANSLATION - - translatiion_path = ( - Path(__file__).parent.parent.parent / "load_" / "loader_classes" / "translation.json" - ) - with translatiion_path.open() as translation_file: - TRANSLATION = json.load(translation_file) - - -TRANSLATION = {} - -_load_translation() diff --git a/raillabel/format/understand_ai/bounding_box_2d.py b/raillabel/format/understand_ai/bounding_box_2d.py deleted file mode 100644 index 2e86cfd..0000000 --- a/raillabel/format/understand_ai/bounding_box_2d.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from uuid import UUID - -from ._annotation import _Annotation -from .sensor_reference import SensorReference - - -@dataclass -class BoundingBox2d(_Annotation): - """A 2d bounding box. - - Parameters - ---------- - id: uuid.UUID - Unique identifier of the annotation. - object_id: uuid.UUID - Unique identifier of the object this annotation refers to. Used for tracking. - class_name: str - Name of the class this annotation belongs to. - attributes: dict[str, str or list] - Key value pairs of attributes with the keys beeing the friendly identifier of the - attribute and the value beeing the attribute value. - sensor: raillabel.format.understand_ai.SensorReference - Information about the sensor this annotation is labeled in. - x_min: float - Left corner of the bounding box in pixels. - y_min: float - Top corner of the bounding box in pixels. - x_max: float - Right corner of the bounding box in pixels. - y_max: float - Bottom corner of the bounding box in pixels. - """ - - x_min: float - y_min: float - x_max: float - y_max: float - - OPENLABEL_ID = "bbox" - - @classmethod - def fromdict(cls, data_dict: t.Dict) -> "BoundingBox2d": - """Generate a BoundingBox2d from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - BoundingBox2d - Converted 2d bounding box. - """ - - return BoundingBox2d( - id=UUID(data_dict["id"]), - object_id=UUID(data_dict["objectId"]), - class_name=data_dict["className"], - x_min=data_dict["geometry"]["xMin"], - y_min=data_dict["geometry"]["yMin"], - x_max=data_dict["geometry"]["xMax"], - y_max=data_dict["geometry"]["yMax"], - attributes=data_dict["attributes"], - sensor=SensorReference.fromdict(data_dict["sensor"]), - ) - - def _val_to_raillabel(self) -> list: - return [ - (self.x_max + self.x_min) / 2, - (self.y_max + self.y_min) / 2, - abs(self.x_max - self.x_min), - abs(self.y_max - self.y_min), - ] diff --git a/raillabel/format/understand_ai/bounding_box_3d.py b/raillabel/format/understand_ai/bounding_box_3d.py deleted file mode 100644 index 9377695..0000000 --- a/raillabel/format/understand_ai/bounding_box_3d.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from uuid import UUID - -from ._annotation import _Annotation -from .point_3d import Point3d -from .quaternion import Quaternion -from .sensor_reference import SensorReference -from .size_3d import Size3d - - -@dataclass -class BoundingBox3d(_Annotation): - """A 3d bounding box. - - Parameters - ---------- - id: uuid.UUID - Unique identifier of the annotation. - object_id: uuid.UUID - Unique identifier of the object this annotation refers to. Used for tracking. - class_name: str - Name of the class this annotation belongs to. - attributes: dict[str, str or list] - Key value pairs of attributes with the keys beeing the friendly identifier of the - attribute and the value beeing the attribute value. - sensor: raillabel.format.understand_ai.SensorReference - Information about the sensor this annotation is labeled in. - center: raillabel.format.understand_ai.Point3d - Center position of the bounding box. - size: raillabel.format.understand_ai.Size3d - 3d size of the bounding box. - quaternion: raillabel.format.understand_ai.Quaternion - Rotation quaternion of the bounding box. - """ - - center: Point3d - size: Size3d - quaternion: Quaternion - - OPENLABEL_ID = "cuboid" - - @classmethod - def fromdict(cls, data_dict: t.Dict) -> "BoundingBox3d": - """Generate a BoundingBox3d from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - BoundingBox3d - Converted 3d bounding box. - """ - - return BoundingBox3d( - id=UUID(data_dict["id"]), - object_id=UUID(data_dict["objectId"]), - class_name=data_dict["className"], - center=Point3d.fromdict(data_dict["geometry"]["center"]), - size=Size3d.fromdict(data_dict["geometry"]["size"]), - quaternion=Quaternion.fromdict(data_dict["geometry"]["quaternion"]), - attributes=data_dict["attributes"], - sensor=SensorReference.fromdict(data_dict["sensor"]), - ) - - def _val_to_raillabel(self) -> list: - return [ - float(self.center.x), - float(self.center.y), - float(self.center.z), - float(self.quaternion.x), - float(self.quaternion.y), - float(self.quaternion.z), - float(self.quaternion.w), - float(self.size.width), - float(self.size.length), - float(self.size.height), - ] diff --git a/raillabel/format/understand_ai/coordinate_system.py b/raillabel/format/understand_ai/coordinate_system.py deleted file mode 100644 index afe28c4..0000000 --- a/raillabel/format/understand_ai/coordinate_system.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from ._translation import fetch_sensor_resolutions, fetch_sensor_type, translate_sensor_id - - -@dataclass -class CoordinateSystem: - """Global information for a sensor regarding calibration. - - Parameters - ---------- - uid: str - Friendly name of the sensor as well as its identifier. Must be unique - topic: str - Rostopic of the sensor. - frame_id: str - Name of the directory containing the files from this sensor. - position: list of float - 3D translation with regards to the origin. - rotation_quaternion: list of float - Rotation quaternion with regards to the origin. - rotation_matrix: list of float - Rotation matrix with regards to the origin. - angle_axis_rotation: list of float - Angle axis rotation with regards to the origin. - homogeneous_transform: list of float, optional - Homogeneous transformation matrix with regards to the origin. Default is None. - measured_position: list of float, optional - camera_matrix: list of float, optional - Camera matrix of the sensor. Only applies to sensors of type camera. Default is None. - dist_coeffs: list of float, optional - Distortion coefficients of the sensor. Only applies to sensors of type camera. Default is - None. - """ - - uid: str - topic: str - frame_id: str - position: t.List[float] - rotation_quaternion: t.List[float] - rotation_matrix: t.List[float] - angle_axis_rotation: t.List[float] - homogeneous_transform: t.Optional[t.List[float]] = None - measured_position: t.Optional[t.List[float]] = None - camera_matrix: t.Optional[t.List[float]] = None - dist_coeffs: t.Optional[t.List[float]] = None - - @property - def translated_uid(self) -> str: - """Return uid translated to raillabel.""" - return translate_sensor_id(self.uid) - - @classmethod - def fromdict(cls, data_dict: dict) -> "CoordinateSystem": - """Generate a CoordinateSystem from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data. - - Returns - ------- - coordinate_system: CoordinateSystem - Converted coordinate_system. - """ - - return CoordinateSystem( - uid=data_dict["coordinate_system_id"], - topic=data_dict["topic"], - frame_id=data_dict["frame_id"], - position=data_dict["position"], - rotation_quaternion=data_dict["rotation_quaternion"], - rotation_matrix=data_dict["rotation_matrix"], - angle_axis_rotation=data_dict["angle_axis_rotation"], - homogeneous_transform=data_dict.get("homogeneous_transform"), - measured_position=data_dict.get("measured_position"), - camera_matrix=data_dict.get("camera_matrix"), - dist_coeffs=data_dict.get("dist_coeffs"), - ) - - def to_raillabel(self) -> t.Tuple[dict, dict]: - """Convert to a raillabel compatible dict. - - Returns - ------- - coordinate_system_dict: dict - Dictionary of the raillabel coordinate system. - stream_dict: dict - Dictionary of the raillabel stream. - """ - - stream_dict = { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": { - "translation": self.position, - "quaternion": self.rotation_quaternion, - }, - } - - coordinate_system_dict = { - "type": fetch_sensor_type(self.translated_uid), - "uri": self.topic, - "stream_properties": self._stream_properties_to_raillabel( - fetch_sensor_type(self.translated_uid) - ), - } - - if coordinate_system_dict["stream_properties"] is None: - del coordinate_system_dict["stream_properties"] - - return stream_dict, coordinate_system_dict - - def _stream_properties_to_raillabel(self, type: str) -> t.Optional[dict]: - - if type == "camera": - return { - "intrinsics_pinhole": { - "camera_matrix": self._convert_camera_matrix(self.camera_matrix[:]), - "distortion_coeffs": self.dist_coeffs, - "width_px": fetch_sensor_resolutions(self.translated_uid)["x"], - "height_px": fetch_sensor_resolutions(self.translated_uid)["y"], - } - } - - elif type == "radar": - return { - "intrinsics_radar": { - "resolution_px_per_m": fetch_sensor_resolutions(self.translated_uid)[ - "resolution_px_per_m" - ], - "width_px": fetch_sensor_resolutions(self.translated_uid)["x"], - "height_px": fetch_sensor_resolutions(self.translated_uid)["y"], - } - } - - else: - return None - - def _convert_camera_matrix(self, camera_matrix: list) -> list: - - camera_matrix.insert(9, 0) - camera_matrix.insert(6, 0) - camera_matrix.insert(3, 0) - - return camera_matrix diff --git a/raillabel/format/understand_ai/frame.py b/raillabel/format/understand_ai/frame.py deleted file mode 100644 index 7c8ed35..0000000 --- a/raillabel/format/understand_ai/frame.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -import uuid -from dataclasses import dataclass -from decimal import Decimal - -from ..._util._warning import _warning -from ._annotation import _Annotation -from ._translation import translate_class_id, translate_sensor_id -from .bounding_box_2d import BoundingBox2d -from .bounding_box_3d import BoundingBox3d -from .polygon_2d import Polygon2d -from .polyline_2d import Polyline2d -from .segmentation_3d import Segmentation3d - - -@dataclass -class Frame: - """A container of dynamic, timewise, information. - - Parameters - ---------- - id: int - Numerical identifier of the frame. Must be unique within the scene. - timestamp: decimal.Decimal - Timestamp containing the Unix epoch time of the frame with up to nanosecond precision. - annotations: dict - Dictionary containing all annotations. The keys are the uids of the annotations and the - values are objects of type BoundingBox2d, BoundingBox3d, Polygon2d, Polyline2d or - Segementation3d. - """ - - id: int - timestamp: Decimal - bounding_box_2ds: t.Dict[str, BoundingBox2d] - bounding_box_3ds: t.Dict[str, BoundingBox3d] - polygon_2ds: t.Dict[str, Polygon2d] - polyline_2ds: t.Dict[str, Polyline2d] - segmentation_3ds: t.Dict[str, Segmentation3d] - - _annotation_uids: t.Set[str] = None - - @property - def annotations(self) -> dict: - """Return all annotations of this frame in one dict.""" - return { - **self.bounding_box_2ds, - **self.polyline_2ds, - **self.polygon_2ds, - **self.bounding_box_3ds, - **self.segmentation_3ds, - } - - @property - def translated_objects(self) -> dict: - """Return all objects in this frame and translate them. - - Returns - ------- - dict - Dictionary containing all objects. Keys are the object IDs and values are the - translated class names. - """ - return { - str(a.object_id): translate_class_id(a.class_name) for a in self.annotations.values() - } - - @property - def translated_sensors(self) -> dict: - """Return all sensors in this frame and translates them. - - Returns - ------- - dict - Dictionary containing all sensors. Keys are the translated sensor IDs and values are - the SensorReference objects. - """ - sensors_list = [] - - for annotation in list(self.annotations.values()): - sensors_list.append(annotation.sensor) - sensors_list[-1].type = translate_sensor_id(sensors_list[-1].type) - - return {sensor.type: sensor for sensor in sensors_list} - - @classmethod - def fromdict(cls, data_dict: dict) -> "Frame": - """Generate a Frame from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Frame - Converted frame. - """ - - cls._annotation_uids = set() - - return Frame( - id=int(data_dict["frameId"]), - timestamp=Decimal(data_dict["timestamp"]), - bounding_box_2ds=cls._annotation_fromdict( - data_dict["annotations"]["2D_BOUNDING_BOX"], BoundingBox2d - ), - bounding_box_3ds=cls._annotation_fromdict( - data_dict["annotations"]["3D_BOUNDING_BOX"], BoundingBox3d - ), - polygon_2ds=cls._annotation_fromdict(data_dict["annotations"]["2D_POLYGON"], Polygon2d), - polyline_2ds=cls._annotation_fromdict( - data_dict["annotations"]["2D_POLYLINE"], Polyline2d - ), - segmentation_3ds=cls._annotation_fromdict( - data_dict["annotations"]["3D_SEGMENTATION"], Segmentation3d - ), - ) - - def to_raillabel(self) -> dict: - """Generate a Frame from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Frame - Converted frame. - """ - return { - "frame_properties": self._frame_properties_to_raillabel(), - "objects": self._objects_to_raillabel(), - } - - @classmethod - def _annotation_fromdict( - cls, data_dict: dict, annotation_class: t.Type[_Annotation] - ) -> t.Dict[str, t.Type[_Annotation]]: - - annotations = {} - for annotation_dict in data_dict: - annotation_dict["id"] = cls._check_duplicate_annotation_uid(annotation_dict["id"]) - annotations[annotation_dict["id"]] = annotation_class.fromdict(annotation_dict) - - return {ann["id"]: annotation_class.fromdict(ann) for ann in data_dict} - - @classmethod - def _check_duplicate_annotation_uid(cls, uid: str) -> str: - - if uid in cls._annotation_uids: - _warning( - f"Annotation uid {uid} is contained more than once. A new uid will be assigned." - ) - return str(uuid.uuid4()) - - cls._annotation_uids.add(uid) - return uid - - def _frame_properties_to_raillabel(self) -> dict: - - streams_dict = {} - for stream_id, stream in self.translated_sensors.items(): - streams_dict[stream_id] = { - "stream_properties": {"sync": {"timestamp": str(stream.timestamp)}}, - "uri": stream.uri.split("/")[-1], - } - - return { - "timestamp": str(self.timestamp), - "streams": { - sensor.type: sensor.to_raillabel()[1] for sensor in self.translated_sensors.values() - }, - } - - def _objects_to_raillabel(self) -> dict: - object_data = {} - - for annotation in self.annotations.values(): - - object_id = str(annotation.object_id) - - if object_id not in object_data: - object_data[object_id] = {"object_data": {}} - - if annotation.OPENLABEL_ID not in object_data[object_id]["object_data"]: - object_data[object_id]["object_data"][annotation.OPENLABEL_ID] = [] - - object_data[object_id]["object_data"][annotation.OPENLABEL_ID].append( - annotation.to_raillabel()[0] - ) - - return object_data diff --git a/raillabel/format/understand_ai/metadata.py b/raillabel/format/understand_ai/metadata.py deleted file mode 100644 index 2a2a5a4..0000000 --- a/raillabel/format/understand_ai/metadata.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import json -from dataclasses import dataclass -from pathlib import Path - - -@dataclass -class Metadata: - """Container for metadata information about the scene itself. - - Parameters - ---------- - clip_id: str - Identifier of the scene for internal purposes. - external_clip_id: str - Identifier of the scene for external purposes. - project_id: str - Identifier of the annotation project. - export_time: str - Timestamp of the export in the format 'YYYY-MM-DD hh:mm UTC'. - exporter_version: str - Version of the Understand.AI-exporter. - coordinate_system_3d: str - coordinate_system_reference: str - folder_name: str - Directory with the exported reference data (e.g. images, point clouds). - """ - - clip_id: str - external_clip_id: str - project_id: str - export_time: str - exporter_version: str - coordinate_system_3d: str - coordinate_system_reference: str - folder_name: str - - @classmethod - def fromdict(cls, data_dict: dict) -> "Metadata": - """Generate a Metadata from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - metadata: Metadata - Converted metadata. - """ - - return Metadata( - clip_id=data_dict["clip_id"], - external_clip_id=data_dict["external_clip_id"], - project_id=data_dict["project_id"], - export_time=data_dict["export_time"], - exporter_version=data_dict["exporter_version"], - coordinate_system_3d=data_dict["coordinate_system_3d"], - coordinate_system_reference=data_dict["coordinate_system_reference"], - folder_name=data_dict["folder_name"], - ) - - def to_raillabel(self) -> dict: - """Convert to a raillabel compatible dict. - - Returns - ------- - metadata: dict - Converted metadata. - """ - - return { - "name": self.external_clip_id, - "schema_version": "1.0.0", - "subschema_version": self._get_subschema_version(), - "tagged_file": self.folder_name, - "annotator": "understandAI GmbH", - } - - def _get_subschema_version(self) -> str: - RAILLABEL_SCHEMA_PATH = ( - Path(__file__).parent.parent.parent / "validate" / "schemas" / "raillabel_schema.json" - ) - - with RAILLABEL_SCHEMA_PATH.open() as schema_file: - subschema_version = json.load(schema_file)["version"] - - return subschema_version diff --git a/raillabel/format/understand_ai/point_3d.py b/raillabel/format/understand_ai/point_3d.py deleted file mode 100644 index 41e90ec..0000000 --- a/raillabel/format/understand_ai/point_3d.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Point3d: - """Dimensional information of an object in 3d. - - Parameters - ---------- - x: float - Position of the object in the x-dimension. - y: float - Position of the object in the y-dimension. - z: float - Position of the object in the z-dimension. - """ - - x: float - y: float - z: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Point3d": - """Generate a Point3d from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Point3d - Converted 3d point. - """ - - return Point3d( - x=float(data_dict["x"]), - y=float(data_dict["y"]), - z=float(data_dict["z"]), - ) diff --git a/raillabel/format/understand_ai/polygon_2d.py b/raillabel/format/understand_ai/polygon_2d.py deleted file mode 100644 index 0d0ca42..0000000 --- a/raillabel/format/understand_ai/polygon_2d.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from uuid import UUID - -from ._annotation import _Annotation -from .sensor_reference import SensorReference - - -@dataclass -class Polygon2d(_Annotation): - """A 2d polygon. - - Parameters - ---------- - id: uuid.UUID - Unique identifier of the annotation. - object_id: uuid.UUID - Unique identifier of the object this annotation refers to. Used for tracking. - class_name: str - Name of the class this annotation belongs to. - attributes: dict[str, str or list] - Key value pairs of attributes with the keys beeing the friendly identifier of the - attribute and the value beeing the attribute value. - sensor: raillabel.format.understand_ai.SensorReference - Information about the sensor this annotation is labeled in. - points: list[tuple[float, float]] - 2d points belonging to the polygon. - """ - - points: t.List[t.Tuple[float, float]] - - OPENLABEL_ID = "poly2d" - - @classmethod - def fromdict(cls, data_dict: t.Dict) -> "Polygon2d": - """Generate a Polygon2d from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Polygon2d - Converted 2d polygon. - """ - - return Polygon2d( - id=UUID(data_dict["id"]), - object_id=UUID(data_dict["objectId"]), - class_name=data_dict["className"], - attributes=data_dict["attributes"], - sensor=SensorReference.fromdict(data_dict["sensor"]), - points=[(p[0], p[1]) for p in data_dict["geometry"]["points"]], - ) - - def to_raillabel(self) -> t.Tuple[dict, str, str, dict]: - """Convert to a raillabel compatible dict. - - Returns - ------- - annotation: dict - Dictionary valid for the raillabel schema. - object_id: str - Friendly identifier of the object this sensor belongs to. - class_name: str - Friendly identifier of the class the annotated object belongs to. - sensor_reference: dict - Dictionary of the sensor reference. - """ - - polygon = super().to_raillabel() - polygon[0]["closed"] = True - polygon[0]["mode"] = "MODE_POLY2D_ABSOLUTE" - - return polygon - - def _val_to_raillabel(self) -> t.List[float]: - return [coordinate for point in self.points for coordinate in point] diff --git a/raillabel/format/understand_ai/polyline_2d.py b/raillabel/format/understand_ai/polyline_2d.py deleted file mode 100644 index 861fed3..0000000 --- a/raillabel/format/understand_ai/polyline_2d.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from uuid import UUID - -from ._annotation import _Annotation -from .sensor_reference import SensorReference - - -@dataclass -class Polyline2d(_Annotation): - """A 2d polyline. - - Parameters - ---------- - id: uuid.UUID - Unique identifier of the annotation. - object_id: uuid.UUID - Unique identifier of the object this annotation refers to. Used for tracking. - class_name: str - Name of the class this annotation belongs to. - attributes: dict[str, str or list] - Key value pairs of attributes with the keys beeing the friendly identifier of the - attribute and the value beeing the attribute value. - sensor: raillabel.format.understand_ai.SensorReference - Information about the sensor this annotation is labeled in. - points: list[tuple[float, float]] - 2d points belonging to the polyline. - """ - - points: t.List[t.Tuple[float, float]] - - OPENLABEL_ID = "poly2d" - - @classmethod - def fromdict(cls, data_dict: t.Dict) -> "Polyline2d": - """Generate a Polyline2d from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Polyline2d - Converted 2d polyline. - """ - - return Polyline2d( - id=UUID(data_dict["id"]), - object_id=UUID(data_dict["objectId"]), - class_name=data_dict["className"], - attributes=data_dict["attributes"], - sensor=SensorReference.fromdict(data_dict["sensor"]), - points=[(p[0], p[1]) for p in data_dict["geometry"]["points"]], - ) - - def to_raillabel(self) -> t.Tuple[dict, str, str, dict]: - """Convert to a raillabel compatible dict. - - Returns - ------- - annotation: dict - Dictionary valid for the raillabel schema. - object_id: str - Friendly identifier of the object this sensor belongs to. - class_name: str - Friendly identifier of the class the annotated object belongs to. - sensor_reference: dict - Dictionary of the sensor reference. - """ - - polyline = super().to_raillabel() - polyline[0]["closed"] = False - polyline[0]["mode"] = "MODE_POLY2D_ABSOLUTE" - - return polyline - - def _val_to_raillabel(self) -> t.List[float]: - return [coordinate for point in self.points for coordinate in point] diff --git a/raillabel/format/understand_ai/quaternion.py b/raillabel/format/understand_ai/quaternion.py deleted file mode 100644 index eec6300..0000000 --- a/raillabel/format/understand_ai/quaternion.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Quaternion: - """Dimensional information of an object in 3d. - - Parameters - ---------- - x: float - The x component of the quaternion. - y: float - The y component of the quaternion. - z: float - The z component of the quaternion. - w: float - The w component of the quaternion. - """ - - x: float - y: float - z: float - w: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Quaternion": - """Generate a Quaternion from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Quaternion - Converted quaternion. - """ - - return Quaternion( - x=float(data_dict["x"]), - y=float(data_dict["y"]), - z=float(data_dict["z"]), - w=float(data_dict["w"]), - ) diff --git a/raillabel/format/understand_ai/scene.py b/raillabel/format/understand_ai/scene.py deleted file mode 100644 index 3d589c2..0000000 --- a/raillabel/format/understand_ai/scene.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass - -from ..._util._warning import _warning -from .coordinate_system import CoordinateSystem -from .frame import Frame -from .metadata import Metadata - - -@dataclass -class Scene: - """The root Understand.Ai class, which contains all data. - - Parameters - ---------- - metadata: raillabel.format.understand_ai.Metadata - Container for metadata information about the scene itself. - coordinate_systems: dict[str, raillabel.format.understand_ai.CoordinateSystem] - Global information for sensors regarding calibration. - frames: dict[int, raillabel.format.understand_ai.Frame] - """ - - metadata: Metadata - coordinate_systems: t.Dict[str, CoordinateSystem] - frames: t.Dict[int, Frame] - - @classmethod - def fromdict(cls, data_dict: dict) -> "Scene": - """Generate a Scene from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Scene - Converted scene. - """ - - return Scene( - metadata=Metadata.fromdict(data_dict["metadata"]), - coordinate_systems=cls._coordinate_systems_fromdict(data_dict["coordinateSystems"]), - frames=cls._frames_fromdict(data_dict["frames"]), - ) - - def to_raillabel(self) -> dict: - """Convert to a raillabel compatible dict. - - Returns - ------- - dict: - Dictionary of the raillabel scene. - """ - return { - "openlabel": { - "metadata": self.metadata.to_raillabel(), - "streams": self._streams_to_raillabel(), - "coordinate_systems": self._coordinate_systems_to_raillabel(), - "objects": self._objects_to_raillabel(), - "frames": {str(frame.id): frame.to_raillabel() for frame in self.frames.values()}, - } - } - - @classmethod - def _coordinate_systems_fromdict(cls, data_dict: t.List[dict]) -> t.Dict[str, CoordinateSystem]: - coordinate_systems = {} - for cs in data_dict: - coordinate_systems[cs["coordinate_system_id"]] = CoordinateSystem.fromdict(cs) - - return coordinate_systems - - @classmethod - def _frames_fromdict(cls, data_dict: t.List[dict]) -> t.Dict[int, Frame]: - frames = {} - for frame in data_dict: - frame_id = int(frame["frameId"]) - - if frame_id in frames: - _warning( - f"Frame UID {frame_id} is contained more than once in the scene. " - + "The duplicate frame will be omitted." - ) - continue - - frames[frame_id] = Frame.fromdict(frame) - - return frames - - def _streams_to_raillabel(self) -> dict: - return {cs.translated_uid: cs.to_raillabel()[1] for cs in self.coordinate_systems.values()} - - def _coordinate_systems_to_raillabel(self) -> dict: - coordinate_systems = { - cs.translated_uid: cs.to_raillabel()[0] for cs in self.coordinate_systems.values() - } - - coordinate_systems["base"] = { - "type": "local", - "parent": "", - "children": list(coordinate_systems.keys()), - } - - return coordinate_systems - - def _objects_to_raillabel(self) -> dict: - - object_dicts = self._collect_all_translated_objects() - - object_name_counter = {} - objects = {} - for object_id, object_class in object_dicts.items(): - - if object_class not in object_name_counter: - object_name_counter[object_class] = 0 - - objects[object_id] = { - "name": f"{object_class}_{str(object_name_counter[object_class]).rjust(4, '0')}", - "type": object_class, - } - object_name_counter[object_class] += 1 - - return objects - - def _collect_all_translated_objects(self) -> dict: - object_dicts = {} - for frame in self.frames.values(): - object_dicts.update(frame.translated_objects) - - return object_dicts diff --git a/raillabel/format/understand_ai/segmentation_3d.py b/raillabel/format/understand_ai/segmentation_3d.py deleted file mode 100644 index 88edaf4..0000000 --- a/raillabel/format/understand_ai/segmentation_3d.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from uuid import UUID - -from ._annotation import _Annotation -from .sensor_reference import SensorReference - - -@dataclass -class Segmentation3d(_Annotation): - """The 3D segmentation of a lidar pointcloud. - - Parameters - ---------- - id: uuid.UUID - Unique identifier of the annotation. - object_id: uuid.UUID - Unique identifier of the object this annotation refers to. Used for tracking. - class_name: str - Name of the class this annotation belongs to. - attributes: dict[str, str or list] - Key value pairs of attributes with the keys beeing the friendly identifier of the - attribute and the value beeing the attribute value. - sensor: raillabel.format.understand_ai.SensorReference - Information about the sensor this annotation is labeled in. - associated_points: list[int] - List of point indices of the lidar pointcloud. - number_of_points: int - Total number of points in the associated_points. - """ - - associated_points: t.List[int] - number_of_points: int - - OPENLABEL_ID = "vec" - - @classmethod - def fromdict(cls, data_dict: t.Dict) -> "Segmentation3d": - """Generate a Segmentation3d from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Segmentation3d - Converted 3d segmentation. - """ - - return Segmentation3d( - id=UUID(data_dict["id"]), - object_id=UUID(data_dict["objectId"]), - class_name=data_dict["className"], - attributes=data_dict["attributes"], - sensor=SensorReference.fromdict(data_dict["sensor"]), - associated_points=data_dict["geometry"]["associatedPoints"], - number_of_points=data_dict["geometry"]["numberOfPointsInBox"], - ) - - def _val_to_raillabel(self) -> list: - return self.associated_points diff --git a/raillabel/format/understand_ai/sensor_reference.py b/raillabel/format/understand_ai/sensor_reference.py deleted file mode 100644 index 5df859c..0000000 --- a/raillabel/format/understand_ai/sensor_reference.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from dataclasses import dataclass -from decimal import Decimal - - -@dataclass -class SensorReference: - """Information for a sensor in a frame. - - Parameters - ---------- - type: str - Friendly name of the sensor and its unique identifier. - uri: str - URI to the file containing the frame specific sensor output from the project directory. - timestamp: decimal.Decimal - Unix timestamp of the sensor recording. - """ - - type: str - uri: str - timestamp: Decimal - - @classmethod - def fromdict(cls, data_dict: dict) -> "SensorReference": - """Generate a SensorReference from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - SensorReference - Converted sensor reference. - """ - - return SensorReference( - type=data_dict["type"], uri=data_dict["uri"], timestamp=Decimal(data_dict["timestamp"]) - ) - - def to_raillabel(self) -> t.Tuple[str, dict]: - """Convert to a raillabel compatible dict. - - Returns - ------- - sensor_id: str - Friendly identifier of the sensor. - sensor_reference: dict - Dictionary valid for the raillabel schema. - """ - - return ( - self.type, - { - "stream_properties": {"sync": {"timestamp": str(self.timestamp)}}, - "uri": self.uri.split("/")[-1], - }, - ) diff --git a/raillabel/format/understand_ai/size_3d.py b/raillabel/format/understand_ai/size_3d.py deleted file mode 100644 index 6be56a4..0000000 --- a/raillabel/format/understand_ai/size_3d.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from dataclasses import dataclass - - -@dataclass -class Size3d: - """Dimensional information of an object in 3d. - - Parameters - ---------- - width: float - Size of the object in the x-dimension. - length: float - Size of the object in the y-dimension. - height: float - Size of the object in the z-dimension. - """ - - width: float - length: float - height: float - - @classmethod - def fromdict(cls, data_dict: dict) -> "Size3d": - """Generate a Size3d from a dictionary in the UAI format. - - Parameters - ---------- - data_dict: dict - Understand.AI T4 format dictionary containing the data_dict. - - Returns - ------- - Size3d - Converted 3d size. - """ - - return Size3d( - width=float(data_dict["width"]), - length=float(data_dict["length"]), - height=float(data_dict["height"]), - ) diff --git a/raillabel/json_format/__init__.py b/raillabel/json_format/__init__.py new file mode 100644 index 0000000..9ad9f51 --- /dev/null +++ b/raillabel/json_format/__init__.py @@ -0,0 +1,65 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 +"""Package for classes representing the direct RailLabel JSON objects.""" + +from .attributes import JSONAttributes +from .bbox import JSONBbox +from .boolean_attribute import JSONBooleanAttribute +from .coordinate_system import JSONCoordinateSystem +from .cuboid import JSONCuboid +from .element_data_pointer import JSONElementDataPointer +from .frame import JSONFrame, JSONFrameData, JSONFrameProperties +from .frame_interval import JSONFrameInterval +from .metadata import JSONMetadata +from .num import JSONNum +from .num_attribute import JSONNumAttribute +from .object import JSONObject +from .object_data import JSONAnnotations, JSONObjectData +from .poly2d import JSONPoly2d +from .poly3d import JSONPoly3d +from .scene import JSONScene, JSONSceneContent +from .stream_camera import JSONIntrinsicsPinhole, JSONStreamCamera, JSONStreamCameraProperties +from .stream_other import JSONStreamOther +from .stream_radar import JSONIntrinsicsRadar, JSONStreamRadar, JSONStreamRadarProperties +from .stream_sync import JSONStreamSync, JSONStreamSyncProperties, JSONStreamSyncTimestamp +from .text_attribute import JSONTextAttribute +from .transform_data import JSONTransformData +from .vec import JSONVec +from .vec_attribute import JSONVecAttribute + +__all__ = [ + "JSONAnnotations", + "JSONAttributes", + "JSONBbox", + "JSONBooleanAttribute", + "JSONCoordinateSystem", + "JSONCuboid", + "JSONElementDataPointer", + "JSONFrameInterval", + "JSONFrame", + "JSONFrameData", + "JSONFrameProperties", + "JSONMetadata", + "JSONNumAttribute", + "JSONNum", + "JSONObjectData", + "JSONObject", + "JSONPoly2d", + "JSONPoly3d", + "JSONScene", + "JSONSceneContent", + "JSONStreamCamera", + "JSONStreamCameraProperties", + "JSONIntrinsicsPinhole", + "JSONStreamOther", + "JSONStreamRadar", + "JSONStreamRadarProperties", + "JSONIntrinsicsRadar", + "JSONStreamSync", + "JSONStreamSyncProperties", + "JSONStreamSyncTimestamp", + "JSONTextAttribute", + "JSONTransformData", + "JSONVecAttribute", + "JSONVec", +] diff --git a/raillabel/json_format/_json_format_base.py b/raillabel/json_format/_json_format_base.py new file mode 100644 index 0000000..5095d77 --- /dev/null +++ b/raillabel/json_format/_json_format_base.py @@ -0,0 +1,8 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from pydantic import BaseModel + + +class _JSONFormatBase(BaseModel, extra="forbid"): + pass diff --git a/raillabel/json_format/attributes.py b/raillabel/json_format/attributes.py new file mode 100644 index 0000000..30846e6 --- /dev/null +++ b/raillabel/json_format/attributes.py @@ -0,0 +1,23 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase +from .boolean_attribute import JSONBooleanAttribute +from .num_attribute import JSONNumAttribute +from .text_attribute import JSONTextAttribute +from .vec_attribute import JSONVecAttribute + + +class JSONAttributes(_JSONFormatBase): + """Attributes is the alias of element data that can be nested inside geometric object data. + + For example, a certain bounding box can have attributes related to its score, visibility, etc. + These values can be nested inside the bounding box as attributes. + """ + + boolean: list[JSONBooleanAttribute] | None = None + num: list[JSONNumAttribute] | None = None + text: list[JSONTextAttribute] | None = None + vec: list[JSONVecAttribute] | None = None diff --git a/raillabel/json_format/bbox.py b/raillabel/json_format/bbox.py new file mode 100644 index 0000000..7fd9f6a --- /dev/null +++ b/raillabel/json_format/bbox.py @@ -0,0 +1,32 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +from ._json_format_base import _JSONFormatBase +from .attributes import JSONAttributes + + +class JSONBbox(_JSONFormatBase): + """A 2D bounding box is defined as a 4-dimensional vector [x, y, w, h]. + + [x, y] is the center of the bounding box and [w, h] represent the width (horizontal, + x-coordinate dimension) and height (vertical, y-coordinate dimension), respectively. + """ + + name: str + """This is a string encoding the name of this object data. It is used as index inside the + corresponding object data pointers.""" + + val: tuple[float, float, float, float] + "The array of 4 values that define the [x, y, w, h] values of the bbox." + + coordinate_system: str + "Name of the coordinate system in respect of which this object data is expressed." + + uid: UUID | None = None + "This is a string encoding the Universal Unique identifyer of the annotation." + + attributes: JSONAttributes | None = None diff --git a/raillabel/json_format/boolean_attribute.py b/raillabel/json_format/boolean_attribute.py new file mode 100644 index 0000000..a41ca11 --- /dev/null +++ b/raillabel/json_format/boolean_attribute.py @@ -0,0 +1,17 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase + + +class JSONBooleanAttribute(_JSONFormatBase): + """A boolean attribute.""" + + name: str + """Friendly identifier describing the attribute. Used to track the attribute throughout + annotations and frames.""" + + val: bool + "The boolean value." diff --git a/raillabel/json_format/coordinate_system.py b/raillabel/json_format/coordinate_system.py new file mode 100644 index 0000000..899581d --- /dev/null +++ b/raillabel/json_format/coordinate_system.py @@ -0,0 +1,26 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal + +from ._json_format_base import _JSONFormatBase +from .transform_data import JSONTransformData + + +class JSONCoordinateSystem(_JSONFormatBase): + """A 3D reference frame.""" + + parent: Literal["base", ""] + "This is the string UID of the parent coordinate system this coordinate system is referring to." + + type: Literal["sensor", "local"] + """This is a string that describes the type of the coordinate system, for example, 'local', + 'geo').""" + + pose_wrt_parent: JSONTransformData | None = None + "The transformation with regards to the parent coordinate system." + + children: list[str] | None = None + "List of children of this coordinate system." diff --git a/raillabel/json_format/cuboid.py b/raillabel/json_format/cuboid.py new file mode 100644 index 0000000..0831add --- /dev/null +++ b/raillabel/json_format/cuboid.py @@ -0,0 +1,34 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +from ._json_format_base import _JSONFormatBase +from .attributes import JSONAttributes + + +class JSONCuboid(_JSONFormatBase): + """A cuboid or 3D bounding box. + + It is defined by the position of its center, the rotation in 3D, and its dimensions. + """ + + name: str + """This is a string encoding the name of this object data. It is used as index inside the + corresponding object data pointers.""" + + val: tuple[float, float, float, float, float, float, float, float, float, float] + """List of values encoding the position, rotation and dimensions. It is + (x, y, z, qx, qy, qz, qw, sx, sy, sz) where (x, y, z) encodes the position, (qx, qy, qz, qw) + encodes the quaternion that encode the rotation, and (sx, sy, sz) are the dimensions of the + cuboid in its object coordinate system""" + + coordinate_system: str + "Name of the coordinate system in respect of which this object data is expressed." + + uid: UUID | None = None + "This is a string encoding the Universal Unique identifyer of the annotation." + + attributes: JSONAttributes | None = None diff --git a/raillabel/json_format/element_data_pointer.py b/raillabel/json_format/element_data_pointer.py new file mode 100644 index 0000000..d0dea1d --- /dev/null +++ b/raillabel/json_format/element_data_pointer.py @@ -0,0 +1,30 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal + +from ._json_format_base import _JSONFormatBase +from .frame_interval import JSONFrameInterval + + +class JSONElementDataPointer(_JSONFormatBase): + """A pointer to element data of elements. + + It is indexed by 'name', and containing information about the element data type, for example, + bounding box, cuboid, and the frame intervals in which this element_data exists within an + element. That means, these pointers can be used to explore element data dynamic information + within the JSON content. + """ + + attribute_pointers: dict[str, Literal["num", "text", "boolean", "vec"]] + """This is a JSON object which contains pointers to the attributes of the element data pointed by + this pointer. The attributes pointer keys shall be the 'name' of the attribute of the element + data this pointer points to.""" + + frame_intervals: list[JSONFrameInterval] + "List of frame intervals of the element data pointed by this pointer." + + type: Literal["bbox", "num", "poly2d", "poly3d", "cuboid", "vec"] + "Type of the element data pointed by this pointer." diff --git a/raillabel/json_format/frame.py b/raillabel/json_format/frame.py new file mode 100644 index 0000000..2ec6334 --- /dev/null +++ b/raillabel/json_format/frame.py @@ -0,0 +1,44 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from decimal import Decimal +from uuid import UUID + +from ._json_format_base import _JSONFormatBase +from .num import JSONNum +from .object_data import JSONObjectData +from .stream_sync import JSONStreamSync + + +class JSONFrame(_JSONFormatBase): + """A frame is a container of dynamic, timewise, information.""" + + frame_properties: JSONFrameProperties | None = None + "Container of frame information other than annotations." + + objects: dict[UUID, JSONObjectData] | None = None + """This is a JSON object that contains dynamic information on RailLabel objects. Object keys are + strings containing 32 bytes UUIDs. Object values contain an 'object_data' JSON object.""" + + +class JSONFrameProperties(_JSONFormatBase): + """Container of frame information other than annotations.""" + + timestamp: Decimal | str | None = None + """The timestamp indicates a time instant as a UTC string or numerical value to describe this + frame.""" + + streams: dict[str, JSONStreamSync] | None = None + """Stream timestamps for synchronization.""" + + frame_data: JSONFrameData | None = None + "Additional data to describe attributes of the frame (like GPS position)." + + +class JSONFrameData(_JSONFormatBase): + """Additional data to describe attributes of the frame (like GPS position).""" + + num: list[JSONNum] | None = None + "List of 'num' that describe this frame." diff --git a/raillabel/json_format/frame_interval.py b/raillabel/json_format/frame_interval.py new file mode 100644 index 0000000..984065c --- /dev/null +++ b/raillabel/json_format/frame_interval.py @@ -0,0 +1,19 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase + + +class JSONFrameInterval(_JSONFormatBase): + """A frame interval defines a starting and ending frame number as a closed interval. + + That means the interval includes the limit frame numbers. + """ + + frame_start: int + "Initial frame number of the interval." + + frame_end: int + "Ending frame number of the interval." diff --git a/raillabel/json_format/metadata.py b/raillabel/json_format/metadata.py new file mode 100644 index 0000000..2862a25 --- /dev/null +++ b/raillabel/json_format/metadata.py @@ -0,0 +1,36 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal + +from pydantic import BaseModel + + +class JSONMetadata(BaseModel, extra="allow"): + """Metadata about the annotation file itself.""" + + schema_version: Literal["1.0.0"] + "Version number of the OpenLABEL schema this annotation file follows." + + name: str | None = None + "Name of the OpenLABEL annotation content." + + subschema_version: str | None = None + "Version number of the RailLabel subschema this annotation file follows." + + exporter_version: str | None = None + "Version identifyer of the exporter software." + + file_version: str | None = None + "Version number of the OpenLABEL annotation content." + + tagged_file: str | None = None + "File name or URI of the data file being tagged." + + annotator: str | None = None + "The person or organization responsible for annotating the file." + + comment: str | None = None + "Additional information or description about the annotation content." diff --git a/raillabel/json_format/num.py b/raillabel/json_format/num.py new file mode 100644 index 0000000..ce67b9f --- /dev/null +++ b/raillabel/json_format/num.py @@ -0,0 +1,25 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +from ._json_format_base import _JSONFormatBase + + +class JSONNum(_JSONFormatBase): + """A number.""" + + name: str + """This is a string encoding the name of this object data. It is used as index inside the + corresponding object data pointers.""" + + val: float + "The numerical value of the number." + + coordinate_system: str | None = None + "Name of the coordinate system in respect of which this object data is expressed." + + uid: UUID | None = None + "This is a string encoding the Universal Unique identifyer of the annotation." diff --git a/raillabel/json_format/num_attribute.py b/raillabel/json_format/num_attribute.py new file mode 100644 index 0000000..edfdfcd --- /dev/null +++ b/raillabel/json_format/num_attribute.py @@ -0,0 +1,17 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase + + +class JSONNumAttribute(_JSONFormatBase): + """A number attribute.""" + + name: str + """Friendly identifier describing the attribute. Used to track the attribute throughout + annotations and frames.""" + + val: float + "The number value." diff --git a/raillabel/json_format/object.py b/raillabel/json_format/object.py new file mode 100644 index 0000000..777214e --- /dev/null +++ b/raillabel/json_format/object.py @@ -0,0 +1,29 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase +from .element_data_pointer import JSONElementDataPointer +from .frame_interval import JSONFrameInterval + + +class JSONObject(_JSONFormatBase): + """An object is the main type of annotation element. + + Object is designed to represent spatiotemporal entities, such as physical objects in the real + world. Objects shall have a name and type. Objects may have static and dynamic data. Objects + are the only type of elements that may have geometric data, such as bounding boxes, cuboids, + polylines, images, etc. + """ + + name: str + "Name of the object. It is a friendly name and not used for indexing." + + type: str + "The type of an object defines the class the object corresponds to." + + frame_intervals: list[JSONFrameInterval] | None = None + "The array of frame intervals where this object exists or is defined." + + object_data_pointers: dict[str, JSONElementDataPointer] | None = None diff --git a/raillabel/json_format/object_data.py b/raillabel/json_format/object_data.py new file mode 100644 index 0000000..68d4ba9 --- /dev/null +++ b/raillabel/json_format/object_data.py @@ -0,0 +1,27 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase +from .bbox import JSONBbox +from .cuboid import JSONCuboid +from .poly2d import JSONPoly2d +from .poly3d import JSONPoly3d +from .vec import JSONVec + + +class JSONObjectData(_JSONFormatBase): + """Container of annotations of an object in a frame.""" + + object_data: JSONAnnotations + + +class JSONAnnotations(_JSONFormatBase): + """Container of the annotations by type.""" + + bbox: list[JSONBbox] | None = None + cuboid: list[JSONCuboid] | None = None + poly2d: list[JSONPoly2d] | None = None + poly3d: list[JSONPoly3d] | None = None + vec: list[JSONVec] | None = None diff --git a/raillabel/json_format/poly2d.py b/raillabel/json_format/poly2d.py new file mode 100644 index 0000000..3a0ddcd --- /dev/null +++ b/raillabel/json_format/poly2d.py @@ -0,0 +1,38 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal +from uuid import UUID + +from ._json_format_base import _JSONFormatBase +from .attributes import JSONAttributes + + +class JSONPoly2d(_JSONFormatBase): + """A 2D polyline defined as a sequence of 2D points.""" + + name: str + """This is a string encoding the name of this object data. It is used as index inside the + corresponding object data pointers.""" + + val: list[float | str] + "List of numerical values of the polyline, according to its mode." + + closed: bool + """A boolean that defines whether the polyline is closed or not. In case it is closed, it is + assumed that the last point of the sequence is connected with the first one.""" + + mode: Literal["MODE_POLY2D_ABSOLUTE"] + """Mode of the polyline describes how the points should be arranged in the images. + MODE_POLY2D_ABSOLUTE means that any point defined by an x-value followed by a y-value is the + absolute position.""" + + coordinate_system: str + "Name of the coordinate system in respect of which this object data is expressed." + + uid: UUID | None = None + "This is a string encoding the Universal Unique identifyer of the annotation." + + attributes: JSONAttributes | None = None diff --git a/raillabel/json_format/poly3d.py b/raillabel/json_format/poly3d.py new file mode 100644 index 0000000..b185c93 --- /dev/null +++ b/raillabel/json_format/poly3d.py @@ -0,0 +1,32 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +from ._json_format_base import _JSONFormatBase +from .attributes import JSONAttributes + + +class JSONPoly3d(_JSONFormatBase): + """A 3D polyline defined as a sequence of 3D points.""" + + name: str + """This is a string encoding the name of this object data. It is used as index inside the + corresponding object data pointers.""" + + val: list[float] + "List of numerical values of the polyline, according to its mode." + + closed: bool + """A boolean that defines whether the polyline is closed or not. In case it is closed, it is + assumed that the last point of the sequence is connected with the first one.""" + + coordinate_system: str + "Name of the coordinate system in respect of which this object data is expressed." + + uid: UUID | None = None + "This is a string encoding the Universal Unique identifyer of the annotation." + + attributes: JSONAttributes | None = None diff --git a/raillabel/json_format/scene.py b/raillabel/json_format/scene.py new file mode 100644 index 0000000..1a618b0 --- /dev/null +++ b/raillabel/json_format/scene.py @@ -0,0 +1,33 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +from ._json_format_base import _JSONFormatBase +from .coordinate_system import JSONCoordinateSystem +from .frame import JSONFrame +from .frame_interval import JSONFrameInterval +from .metadata import JSONMetadata +from .object import JSONObject +from .stream_camera import JSONStreamCamera +from .stream_other import JSONStreamOther +from .stream_radar import JSONStreamRadar + + +class JSONScene(_JSONFormatBase): + """Root RailLabel object.""" + + openlabel: JSONSceneContent + + +class JSONSceneContent(_JSONFormatBase): + """Container of all scene content.""" + + metadata: JSONMetadata + coordinate_systems: dict[str, JSONCoordinateSystem] | None = None + streams: dict[str, JSONStreamCamera | JSONStreamOther | JSONStreamRadar] | None = None + objects: dict[UUID, JSONObject] | None = None + frames: dict[int, JSONFrame] | None = None + frame_intervals: list[JSONFrameInterval] | None = None diff --git a/raillabel/json_format/stream_camera.py b/raillabel/json_format/stream_camera.py new file mode 100644 index 0000000..fbcd980 --- /dev/null +++ b/raillabel/json_format/stream_camera.py @@ -0,0 +1,55 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal + +from ._json_format_base import _JSONFormatBase + + +class JSONStreamCamera(_JSONFormatBase): + """A stream describes the source of a data sequence, usually a sensor. + + This specific object contains the intrinsics of a camera sensor. + """ + + type: Literal["camera"] + "A string encoding the type of the stream." + + stream_properties: JSONStreamCameraProperties + "Intrinsic calibration of the stream." + + uri: str + "A string encoding the subdirectory containing the sensor files." + + description: str | None = None + "Description of the stream." + + +class JSONStreamCameraProperties(_JSONFormatBase): + """Intrinsic calibration of the stream.""" + + intrinsics_pinhole: JSONIntrinsicsPinhole + + +class JSONIntrinsicsPinhole(_JSONFormatBase): + """JSON object defining an instance of the intrinsic parameters of a pinhole camera.""" + + camera_matrix: tuple[ + float, float, float, float, float, float, float, float, float, float, float, float + ] + """This is a 3x4 camera matrix which projects 3D homogeneous points (4x1) from a camera + coordinate system into the image plane (3x1). This is the usual K matrix for camera projection as + in OpenCV. It is extended from 3x3 to 3x4 to enable its direct utilisation to project 4x1 + homogeneous 3D points. The matrix is defined to follow the camera model: x-to-right, y-down, + z-forward. The following equation applies: x_img = camera_matrix * X_ccs.""" + + distortion_coeffs: tuple[float, float, float, float, float] + "This is the array 1x5 radial and tangential distortion coefficients." + + height_px: int + "Height of the camera output in pixel." + + width_px: int + "Width of the camera output in pixel." diff --git a/raillabel/json_format/stream_other.py b/raillabel/json_format/stream_other.py new file mode 100644 index 0000000..27fee1b --- /dev/null +++ b/raillabel/json_format/stream_other.py @@ -0,0 +1,24 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal + +from ._json_format_base import _JSONFormatBase + + +class JSONStreamOther(_JSONFormatBase): + """A stream describes the source of a data sequence, usually a sensor. + + This specific object describes a sensor without intrinsic calibration. + """ + + type: Literal["lidar", "gps_imu", "other"] + "A string encoding the type of the stream." + + uri: str | None = None + "A string encoding the subdirectory containing the sensor files." + + description: str | None = None + "Description of the stream." diff --git a/raillabel/json_format/stream_radar.py b/raillabel/json_format/stream_radar.py new file mode 100644 index 0000000..67dfceb --- /dev/null +++ b/raillabel/json_format/stream_radar.py @@ -0,0 +1,46 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal + +from ._json_format_base import _JSONFormatBase + + +class JSONStreamRadar(_JSONFormatBase): + """A stream describes the source of a data sequence, usually a sensor. + + This specific object contains the intrinsics of a radar sensor. + """ + + type: Literal["radar"] + "A string encoding the type of the stream." + + stream_properties: JSONStreamRadarProperties + "Intrinsic calibration of the stream." + + uri: str + "A string encoding the subdirectory containing the sensor files." + + description: str | None = None + "Description of the stream." + + +class JSONStreamRadarProperties(_JSONFormatBase): + """Intrinsic calibration of the stream.""" + + intrinsics_radar: JSONIntrinsicsRadar + + +class JSONIntrinsicsRadar(_JSONFormatBase): + """JSON object defining an instance of the intrinsic parameters of a radar.""" + + resolution_px_per_m: float + "Number correlating pixel in the output image to a position relative to the sensor in meters." + + height_px: int + "Height of the radar output in pixel." + + width_px: int + "Width of the radar output in pixel." diff --git a/raillabel/json_format/stream_sync.py b/raillabel/json_format/stream_sync.py new file mode 100644 index 0000000..0c7c95f --- /dev/null +++ b/raillabel/json_format/stream_sync.py @@ -0,0 +1,27 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from decimal import Decimal + +from ._json_format_base import _JSONFormatBase + + +class JSONStreamSync(_JSONFormatBase): + """Syncronization information of a stream in a frame.""" + + stream_properties: JSONStreamSyncProperties + uri: str | None = None + + +class JSONStreamSyncProperties(_JSONFormatBase): + """The sync information.""" + + sync: JSONStreamSyncTimestamp + + +class JSONStreamSyncTimestamp(_JSONFormatBase): + """The timestamp of a stream sync.""" + + timestamp: Decimal | str diff --git a/raillabel/json_format/text_attribute.py b/raillabel/json_format/text_attribute.py new file mode 100644 index 0000000..ddccbbc --- /dev/null +++ b/raillabel/json_format/text_attribute.py @@ -0,0 +1,17 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase + + +class JSONTextAttribute(_JSONFormatBase): + """A text attribute.""" + + name: str + """Friendly identifier describing the attribute. Used to track the attribute throughout + annotations and frames.""" + + val: str + "The text value." diff --git a/raillabel/json_format/transform_data.py b/raillabel/json_format/transform_data.py new file mode 100644 index 0000000..c23addb --- /dev/null +++ b/raillabel/json_format/transform_data.py @@ -0,0 +1,16 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase + + +class JSONTransformData(_JSONFormatBase): + """The translation and rotation of one coordinate system to another.""" + + translation: tuple[float, float, float] + "List of 4 values encoding a quaternion (x, y, z, w)." + + quaternion: tuple[float, float, float, float] + "List of 3 values encoding the translation vector (x, y, z)" diff --git a/raillabel/json_format/vec.py b/raillabel/json_format/vec.py new file mode 100644 index 0000000..1d81162 --- /dev/null +++ b/raillabel/json_format/vec.py @@ -0,0 +1,33 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from typing import Literal +from uuid import UUID + +from ._json_format_base import _JSONFormatBase +from .attributes import JSONAttributes + + +class JSONVec(_JSONFormatBase): + """A vector (list) of numbers.""" + + name: str + """This is a string encoding the name of this object data. It is used as index inside the + corresponding object data pointers.""" + + val: list[float] + "The numerical values of the vector (list) of numbers." + + coordinate_system: str + "Name of the coordinate system in respect of which this object data is expressed." + + uid: UUID | None = None + "This is a string encoding the Universal Unique identifyer of the annotation." + + type: Literal["values", "range"] | None = None + """This attribute specifies whether the vector shall be considered as a descriptor of individual + values or as a definition of a range.""" + + attributes: JSONAttributes | None = None diff --git a/raillabel/json_format/vec_attribute.py b/raillabel/json_format/vec_attribute.py new file mode 100644 index 0000000..9dc1104 --- /dev/null +++ b/raillabel/json_format/vec_attribute.py @@ -0,0 +1,17 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from ._json_format_base import _JSONFormatBase + + +class JSONVecAttribute(_JSONFormatBase): + """A vec attribute.""" + + name: str + """Friendly identifier describing the attribute. Used to track the attribute throughout + annotations and frames.""" + + val: list[float | str] + "The value vector of the attribute." diff --git a/raillabel/load/load.py b/raillabel/load/load.py new file mode 100644 index 0000000..1011347 --- /dev/null +++ b/raillabel/load/load.py @@ -0,0 +1,17 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import json +from pathlib import Path + +from raillabel.format import Scene +from raillabel.json_format import JSONScene + + +def load(path: Path | str) -> Scene: + """Load an annotation file as a scene.""" + with Path(path).open() as annotation_file: + json_data = json.load(annotation_file) + return Scene.from_json(JSONScene(**json_data)) diff --git a/raillabel/load_/__init__.py b/raillabel/load_/__init__.py deleted file mode 100644 index cb4a105..0000000 --- a/raillabel/load_/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 -"""Package containing all necessary modules for loading.""" diff --git a/raillabel/load_/load.py b/raillabel/load_/load.py deleted file mode 100644 index 1f74990..0000000 --- a/raillabel/load_/load.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import json - -from ..exceptions import UnsupportedFormatError -from ..format import Scene -from . import loader_classes as loader_classes_pkg -from .loader_classes._loader_abc import LoaderABC - - -def load(path: str, validate: bool = False, show_warnings: bool = True) -> Scene: - """Load an annotation file of any supported type. - - Parameters - ---------- - path: str - Path to the annotation file. - validate: bool, optional - If True, the annotation data is validated via the respective schema. This is highly - recommended, as not validating the data may lead to errors during loading or while handling - the scene. However, validating may increase the loading time. Default is False. - show_warnings: bool, optional - If True, any non-critical inconsistencies in the data are output as a warning. Default is - True. - - Returns - ------- - scene: raillabel.Scene - Scene with the loaded data. - - Raises - ------ - raillabel.UnsupportedFormatError - if the annotation file does not match any loaders. - raillabel.exceptions.SchemaError - if during the validation, errors in the annotation file are found. - """ - - # To expand the supported formats in a simple manner, load() automatically fetches all classes - # in the loader_classes directory and checks if they are suitable as loaders. To avoid errors - # caused by potential other files and classes in the directory, only classes, which inherite - # from LoaderABC are considered. - - loader_classes = [] - for cls in loader_classes_pkg.__dict__.values(): - if isinstance(cls, type) and issubclass(cls, LoaderABC) and cls != LoaderABC: - loader_classes.append(cls) - - # Checks for the supported file type - if not str(path).lower().endswith(".json"): - raise UnsupportedFormatError(f"{path} is not in a supported file format.") - - # Loads the JSON data - with open(path) as data_file: - data = json.load(data_file) - - # Iterates over the loader classes to find a suitable one - for loader_class in loader_classes: - - loader = loader_class() - - # Checks if the loader supports the data - if not loader.supports(data): - continue - - # Loads the scene - scene = loader.load(data, validate=validate) - - # Outputs the warnings - if show_warnings: - - if len(loader.warnings) > 0: - print(f"During the loading of {path} warnings have occurred:") - - for warning in loader.warnings: - print(" - " + warning) - - return scene - - # This part of the code is only reached if no suitable loader has been found or the data is - # not in a supported file format. Therefore an exception is raised. - - raise UnsupportedFormatError(f"{path} is not in a supported file format.") diff --git a/raillabel/load_/loader_classes/__init__.py b/raillabel/load_/loader_classes/__init__.py deleted file mode 100644 index 190e8a8..0000000 --- a/raillabel/load_/loader_classes/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 -"""Package containing the loader classes for all supported formats.""" - -from importlib import import_module -from inspect import isclass -from pathlib import Path -from pkgutil import iter_modules - -# iterate through the modules in the current package -package_dir = str(Path(__file__).resolve().parent) -for _, module_name, _ in iter_modules([package_dir]): - - # import the module and iterate through its attributes - module = import_module(f"{__name__}.{module_name}") - for attribute_name in dir(module): - attribute = getattr(module, attribute_name) - - if isclass(attribute): - # Add the class to this package's variables - globals()[attribute_name] = attribute diff --git a/raillabel/load_/loader_classes/_loader_abc.py b/raillabel/load_/loader_classes/_loader_abc.py deleted file mode 100644 index 446bfc4..0000000 --- a/raillabel/load_/loader_classes/_loader_abc.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from abc import ABC, abstractmethod -from pathlib import Path - -from ... import format -from ...exceptions import SchemaError -from ...validate.validate import validate as global_validate - - -class LoaderABC(ABC): - """Abstract base class of the annotation file loaders. - - For every annotation format, that can be loaded via raillabel, a loader class should exists, - that inherites from this class. - - Attributes - ---------- - scene: raillabel.Scene - Loaded raillabel.Scene with the data. - warnings: t.List[str] - List of warning strings, that have been found during the execution of load(). - SCHEMA_PATH: Path - Absolute path to the JSON schema. - """ - - scene: format.Scene - warnings: t.List[str] - SCHEMA_PATH: Path - - @abstractmethod - def load(self, data: dict, validate: bool = True) -> format.Scene: - """Load JSON-data into a raillabel.Scene. - - Any non-critical errors are stored in the warnings-property. - - Parameters - ---------- - data: dict - A dictionary loaded from a JSON-file. - validate: bool - If True, the annotation data is validated via the respective schema. This is highly - recommended, as not validating the data may lead to Errors during loading or while - handling the scene. However, validating may increase the loading time. Default is True. - - Returns - ------- - scene: raillabel.Scene - The loaded scene with the data. - - Raises - ------ - raillabel.exceptions.SchemaError - if validate is True and the data does not validate against the schema. - """ - raise NotImplementedError - - @abstractmethod - def supports(self, data: dict) -> bool: - """Determine if the loader class is suitable for the data. - - This is performed based on hints in the data structure and can therefore be done - efficiently. - - Parameters - ---------- - data: dict - A dictionary loaded from a JSON-file. - - Returns - ------- - bool: - If True, the Loader class is suitable for the data. - """ - raise NotImplementedError - - def validate(self, data: dict) -> t.Tuple[bool, t.List[str]]: - """Validate JSON-data with the corresponding schema. - - Parameters - ---------- - data: dict - JSON data to be validated. - - Raises - ------ - raillabel.exceptions.SchemaError - if the schema is not valid. - """ - - is_data_valid, schema_errors = global_validate(data, str(self.SCHEMA_PATH)) - if not is_data_valid: - - error_msg = ( - "The loaded data does not validate against the schema. Errors in the schema:\n" - ) - - for err in schema_errors: - error_msg += " - " + err + "\n" - - raise SchemaError(error_msg) diff --git a/raillabel/load_/loader_classes/loader_raillabel.py b/raillabel/load_/loader_classes/loader_raillabel.py deleted file mode 100644 index 399089c..0000000 --- a/raillabel/load_/loader_classes/loader_raillabel.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import json -import typing as t -from pathlib import Path - -from ... import exceptions, format -from ..._util._warning import _WarningsLogger -from ._loader_abc import LoaderABC - - -class LoaderRailLabel(LoaderABC): - """Loader class for the OpenLabel v1 annotation format. - - Attributes - ---------- - scene: raillabel.Scene - Loaded raillabel.Scene with the data. - warnings: t.List[str] - List of warning strings, that have been found during the execution of load(). - """ - - scene: format.Scene - warnings: t.List[str] - - SCHEMA_PATH: Path = ( - Path(__file__).parent.parent.parent / "validate" / "schemas" / "raillabel_schema.json" - ) - - @property - def subschema_version(self): - """Return subschema version.""" - with self.SCHEMA_PATH.open() as schema_file: - subschema_version = json.load(schema_file)["version"] - return subschema_version - - def load(self, data: dict, validate: bool = False) -> format.Scene: - """Load the data into a raillabel.Scene and return it. - - Parameters - ---------- - data: dict - A dictionary loaded from a JSON-file. - validate: bool, optional - If True, the annotation data is validated via the respective schema. This is - recommended, if you are working with a modified or non-official file. Setting this - option will increase loading time. Default is False. - - Returns - ------- - scene: raillabel.Scene - The loaded scene with the data. - - Raises - ------ - raillabel.exceptions.SchemaError - if validate is True and the data does not validate against the schema. - """ - - if validate: - self.validate(data) - - with _WarningsLogger() as logger: - self.scene = format.Scene.fromdict(data, self.subschema_version) - - self.warnings = logger.warnings - - return self.scene - - def supports(self, data: dict) -> bool: - """Determine if the loader is suitable for the data (lightweight). - - Parameters - ---------- - data: dict - A dictionary loaded from a JSON-file. - - Returns - ------- - bool: - If True, the Loader class is suitable for the data. - """ - - if "openlabel" not in data or "metadata" not in data["openlabel"]: - return False - - if "subschema_version" in data["openlabel"]["metadata"]: - return ( - "openlabel" in data - and "metadata" in data["openlabel"] - and "schema_version" in data["openlabel"]["metadata"] - and data["openlabel"]["metadata"]["subschema_version"].split(".")[0] in ["2", "3"] - ) - - else: - return ( - "openlabel" in data - and "metadata" in data["openlabel"] - and "schema_version" in data["openlabel"]["metadata"] - ) - - def _prepare_data(self, data: dict) -> dict: - """Add optional fields to dict to simplify interaction. - - Parameters - ---------- - data : dict - JSON data. - - Returns - ------- - dict - Enhanced JSON data. - """ - - if "coordinate_systems" not in data["openlabel"]: - data["openlabel"]["coordinate_systems"] = {} - - if "streams" not in data["openlabel"]: - data["openlabel"]["streams"] = {} - - if "objects" not in data["openlabel"]: - data["openlabel"]["objects"] = {} - - if "frames" not in data["openlabel"]: - data["openlabel"]["frames"] = {} - - return data["openlabel"] - - def _check_sensor_completeness(self, cs_data: dict, stream_data: dict): - """Check for corresponding cs and stream completeness. - - Parameters - ---------- - cs_data : dict - Coordinate system data in the RailLabel format. - stream_data : dict - Stream data in the RailLabel format. - - Raises - ------ - raillabel.exceptions.MissingCoordinateSystemError - if a stream has no corresponding coordinate system. - raillabel.exceptions.MissingStreamError - if a coordinate system has no corresponding stream. - raillabel.exceptions.UnsupportedParentError - if a coordinate system has no corresponding stream. - """ - - for stream_uid in stream_data: - if stream_uid not in cs_data: - raise exceptions.MissingCoordinateSystemError( - f"Stream {stream_uid} has no corresponding coordinate system." - ) - - for cs_uid in cs_data: - if cs_uid == "base": - continue - - if cs_data[cs_uid]["parent"] != "base": - raise exceptions.UnsupportedParentError( - f"Only 'base' is permitted as a parent for coordinate system {cs_uid}, " - + f"not {cs_data[cs_uid]['parent']}." - ) - - if cs_uid not in stream_data: - raise exceptions.MissingStreamError( - f"Coordinate sytem {cs_uid} has no corresponding stream." - ) diff --git a/raillabel/load_/loader_classes/loader_understand_ai.py b/raillabel/load_/loader_classes/loader_understand_ai.py deleted file mode 100644 index b15d902..0000000 --- a/raillabel/load_/loader_classes/loader_understand_ai.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from pathlib import Path -from warnings import warn - -from typing_extensions import deprecated - -from ..._util._warning import _WarningsLogger -from ...format import understand_ai as uai_format -from ._loader_abc import LoaderABC -from .loader_raillabel import LoaderRailLabel - - -@deprecated("This class will be moved to `raillabel_providerkit` in all future releases.") -class LoaderUnderstandAi(LoaderABC): - """Loader class for the Understand.Ai Trains4 annotation format. - - Attributes - ---------- - scene: raillabel.format.understand_ai.Scene - Loaded raillabel.format.understand_ai.Scene with the data. - warnings: t.List[str] - List of warning strings, that have been found during the execution of load(). - """ - - scene: uai_format.Scene - warnings: t.List[str] - - SCHEMA_PATH: Path = ( - Path(__file__).parent.parent.parent - / "validate" - / "schemas" - / "understand_ai_t4_schema.json" - ) - - def load(self, data: dict, validate: bool = False) -> uai_format.Scene: - """Load the data into a UAIScene and return it. - - Parameters - ---------- - data: dict - A dictionary loaded from a JSON-file. - validate: bool - If True, the annotation data is validated via the respective schema. This is highly - recommended, as not validating the data may lead to Errors during loading or while - handling the scene. However, validating may increase the loading time. Default is False. - - Returns - ------- - scene: raillabel.format.understand_ai.UAIScene - The loaded scene with the data. - """ - - warn("This class will be moved to `raillabel_providerkit` in all future releases.") - - if validate: - self.validate(data) - - with _WarningsLogger() as logger: - data_converted_to_raillabel = uai_format.Scene.fromdict(data).to_raillabel() - - raillabel_loader = LoaderRailLabel() - raillabel_scene = raillabel_loader.load(data_converted_to_raillabel, validate=False) - - self.warnings = logger.warnings + raillabel_loader.warnings - - return raillabel_scene - - def supports(self, data: dict) -> bool: - """Determine if the loader is suitable for the data (lightweight). - - Parameters - ---------- - data: dict - A dictionary loaded from a JSON-file. - - Returns - ------- - bool: - If True, the Loader class is suitable for the data. - """ - - return ( - "metadata" in data - and "project_id" in data["metadata"] - and "coordinateSystems" in data - and "frames" in data - ) diff --git a/raillabel/load_/loader_classes/translation.json b/raillabel/load_/loader_classes/translation.json deleted file mode 100644 index 1187d34..0000000 --- a/raillabel/load_/loader_classes/translation.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "__version__": "1.1.0", - "classes": { - "2D_catenary_pole": "catenary_pole", - "2D_person": "person", - "2D_bicycle": "bicycle", - "2D_signal_pole": "signal_pole", - "2D_track": "track", - "2D_train": "train", - "2D_train_front": "train_front", - "2D_transition": "transition", - "2D_wagons": "wagons", - "2D_switch": "switch", - "2D_buffer_stop": "buffer_stop", - "2D_crowd": "crowd", - "3D_catenary_pole": "catenary_pole", - "3D_person": "person", - "3D_signal_pole": "signal_pole", - "3D_track": "track", - "3D_train": "train", - "3D_transition": "transition", - "3D_wagons": "wagons", - "3D_switch": "switch", - "3D_buffer_stop": "buffer_stop", - "3D_crowd": "crowd", - "3D_bicycle": "bicycle", - "ir_track": "track", - "ir_transition": "transition", - "ir_wagons": "wagons", - "ir_switch": "switch", - "ir_buffer_stop": "buffer_stop", - "ir_crowd": "crowd", - "ir_bicycle": "bicycle", - "radar_catenary_pole": "catenary_pole", - "radar_person": "person", - "radar_signal_pole": "signal_pole", - "radar_train": "train", - "radar_transition": "transition", - "radar_wagons": "wagons", - "radar_switch": "switch", - "radar_buffer_stop": "buffer_stop", - "radar_crowd": "crowd", - "radar_bicycle": "bicycle", - "rgb_track": "track", - - "Pedestrian": "person", - "Track": "track", - "Trains": "train", - "Transition": "transition", - "CatenaryPoles": "catenary_pole" - }, - "coordinate_systems": { - "new_left_rect": "rgb_highres_left", - "new_middle_rect": "rgb_highres_middle", - "new_right_rect": "rgb_highres_right", - "rgb_left_rect": "rgb_left", - "rgb_middle_rect": "rgb_middle", - "rgb_right_rect": "rgb_right", - "ir_left": "ir_left", - "ir_middle": "ir_middle", - "ir_right": "ir_right", - "lidar_merged": "lidar", - "radar": "radar", - "imu": "gps_imu", - - "S1213751_image": "rgb_highres_left", - "S1213752_image": "rgb_highres_middle", - "S1213755_image": "rgb_highres_right", - "S1206062_image": "rgb_left", - "S1206063_image": "rgb_middle", - "S1206064_image": "rgb_right", - "A0001780_image": "ir_left", - "A0001781_image": "ir_middle", - "A0001782_image": "ir_right", - "points_raw": "lidar", - "S1213751_image_1": "rgb_highres_left", - "S1213752_image_1": "rgb_highres_middle", - "S1213755_image_1": "rgb_highres_right", - "S1206062_image_1": "rgb_left", - "S1206063_image_1": "rgb_middle", - "S1206064_image_1": "rgb_right", - "A0001780_image_1": "ir_left", - "A0001781_image_1": "ir_middle", - "A0001782_image_1": "ir_right", - "points_raw_1": "lidar", - - "base_link": "base", - "S1213751": "rgb_highres_left", - "S1213752": "rgb_highres_middle", - "S1213755": "rgb_highres_right", - "S1206062": "rgb_left", - "S1206063": "rgb_middle", - "S1206064": "rgb_right", - "A0001780": "ir_left", - "A0001781": "ir_middle", - "A0001782": "ir_right", - - "camera_infrared_rectified": "ir_middle", - "camera_long_range_rectified": "rgb_longrange_middle", - "camera_mid_range_rectified": "rgb_middle" - }, - "streams": { - "new_left_rect": "rgb_highres_left", - "new_middle_rect": "rgb_highres_middle", - "new_right_rect": "rgb_highres_right", - "rgb_left_rect": "rgb_left", - "rgb_middle_rect": "rgb_middle", - "rgb_right_rect": "rgb_right", - "ir_left": "ir_left", - "ir_middle": "ir_middle", - "ir_right": "ir_right", - "LIDAR": "lidar", - "lidar_merged": "lidar", - "imu": "gps_imu", - "radar": "radar", - - "S1213751_image": "rgb_highres_left", - "S1213752_image": "rgb_highres_middle", - "S1213755_image": "rgb_highres_right", - "S1206062_image": "rgb_left", - "S1206063_image": "rgb_middle", - "S1206064_image": "rgb_right", - "A0001780_image": "ir_left", - "A0001781_image": "ir_middle", - "A0001782_image": "ir_right", - "points_raw": "lidar", - "S1213751_image_1": "rgb_highres_left", - "S1213752_image_1": "rgb_highres_middle", - "S1213755_image_1": "rgb_highres_right", - "S1206062_image_1": "rgb_left", - "S1206063_image_1": "rgb_middle", - "S1206064_image_1": "rgb_right", - "A0001780_image_1": "ir_left", - "A0001781_image_1": "ir_middle", - "A0001782_image_1": "ir_right", - "points_raw_1": "lidar", - - "base_link": "base", - "S1213751": "rgb_highres_left", - "S1213752": "rgb_highres_middle", - "S1213755": "rgb_highres_right", - "S1206062": "rgb_left", - "S1206063": "rgb_middle", - "S1206064": "rgb_right", - "A0001780": "ir_left", - "A0001781": "ir_middle", - "A0001782": "ir_right", - - "camera_infrared_rectified": "ir_midrange", - "camera_long_range_rectified": "rgb_longrange", - "camera_mid_range_rectified": "rgb_midrange" - }, - "stream_types": { - "rgb_highres_left": "camera", - "rgb_highres_middle": "camera", - "rgb_highres_right": "camera", - "rgb_left": "camera", - "rgb_middle": "camera", - "rgb_right": "camera", - "ir_left": "camera", - "ir_middle": "camera", - "ir_right": "camera", - "radar": "radar", - "lidar": "lidar", - "gps_imu": "gps_imu", - - "ir_midrange": "camera", - "rgb_longrange": "camera", - "rgb_midrange": "camera" - }, - "stream_rostopics": { - "rgb_highres_left": "/S1213751/image", - "rgb_highres_middle": "/S1213752/image", - "rgb_highres_right": "/S1213755/image", - "rgb_left": "/S1206062/image", - "rgb_middle": "/S1206063/image", - "rgb_right": "/S1206064/image", - "ir_left": "/A0001780/image", - "ir_middle": "/A0001781/image", - "ir_right": "/A0001782/image", - "radar": "/talker1/Nvt/Cartesian", - "lidar": "/lidar_merged", - "gps_imu": "/novatel/oem7/inspva" - }, - "stream_resolutions": { - "rgb_highres_left": {"x": 4112,"y": 2504}, - "rgb_highres_middle": {"x": 4112,"y": 2504}, - "rgb_highres_right": {"x": 4112,"y": 2504}, - "rgb_left": {"x": 2464,"y": 1600}, - "rgb_middle": {"x": 2464,"y": 1600}, - "rgb_right": {"x": 2464,"y": 1600}, - "ir_left": {"x": 640,"y": 480}, - "ir_middle": {"x": 640,"y": 480}, - "ir_right": {"x": 640,"y": 480}, - "radar": {"resolution_px_per_m": 2.856, "x": 2856, "y": 1428} - } -} diff --git a/raillabel/load_/loader_classes/translation.json.license b/raillabel/load_/loader_classes/translation.json.license deleted file mode 100644 index fbb8ddf..0000000 --- a/raillabel/load_/loader_classes/translation.json.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: Copyright DB InfraGO AG and the raillabel contributors -SPDX-License-Identifier: Apache-2.0 diff --git a/raillabel/save/save.py b/raillabel/save/save.py index 08bafe6..c5e1e05 100644 --- a/raillabel/save/save.py +++ b/raillabel/save/save.py @@ -1,64 +1,19 @@ # Copyright DB InfraGO AG and contributors # SPDX-License-Identifier: Apache-2.0 -import json -from pathlib import Path - -from .. import exceptions -from ..format import Scene -from ..validate.validate import validate as validate_func - - -def save(scene: Scene, path: str, prettify_json: bool = False, validate: bool = False): - """Save a raillabel.Scene in a JSON file. - - Parameters - ---------- - scene: raillabel.Scene - Scene, which should be saved. - path: str - Path to the file location, that should be used for saving. - save_path: str - Path to the JSON file. - prettify_json: bool, optional - If true, the JSON is saved with linebreaks and indents. This increases readibility but - also the file size. Default is False. - validate: bool, optional - If True, the annotation data is validated via the OpenLabel schema. This is highly - recommended, as not validating the data may lead to Errors during loading or while handling - the scene. However, validating may increase the loading time. Default is False. +from __future__ import annotations - Raises - ------ - SchemaError - If the data does not validate via the OpenLabel schema. - FileNotFoundError - If the save_path does not point to an accessible file. - """ - - path = Path(path) - - # Converts the data stored in the scene into a dictionary - data = scene.asdict() - - # Validates the data - if validate: - is_data_valid, err_msgs = validate_func(data) - if not is_data_valid: - schema_err_msg = ( - "The data could not be saved, because it does not validate " - "against the OpenLabel schema:" - ) +from pathlib import Path - for err_msg in err_msgs: - schema_err_msg += "\n - " + err_msg +from raillabel.format import Scene - raise exceptions.SchemaError(schema_err_msg) - # Saves the data - with path.open("w") as save_file: +def save(scene: Scene, path: Path | str, prettify_json: bool = False) -> None: + """Save a raillabel.Scene to a JSON file.""" + if prettify_json: + json_data = scene.to_json().model_dump_json(exclude_none=True, indent=4) + else: + json_data = scene.to_json().model_dump_json(exclude_none=True) - if prettify_json: - json.dump(data, save_file, indent=4) - else: - json.dump(data, save_file) + with Path(path).open("w") as scene_file: + scene_file.write(json_data) diff --git a/raillabel/scene_builder/__init__.py b/raillabel/scene_builder/__init__.py new file mode 100644 index 0000000..1c5c815 --- /dev/null +++ b/raillabel/scene_builder/__init__.py @@ -0,0 +1,7 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 +"""Package for the scene building functionality.""" + +from .scene_builder import SceneBuilder + +__all__ = ["SceneBuilder"] diff --git a/raillabel/scene_builder/scene_builder.py b/raillabel/scene_builder/scene_builder.py new file mode 100644 index 0000000..feaa56d --- /dev/null +++ b/raillabel/scene_builder/scene_builder.py @@ -0,0 +1,334 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from copy import deepcopy +from dataclasses import dataclass +from decimal import Decimal +from uuid import UUID + +from raillabel.format import ( + Bbox, + Camera, + Cuboid, + Frame, + GpsImu, + IntrinsicsPinhole, + IntrinsicsRadar, + Lidar, + Metadata, + Object, + OtherSensor, + Point2d, + Point3d, + Poly2d, + Poly3d, + Quaternion, + Radar, + Scene, + Seg3d, + Size2d, + Size3d, +) +from raillabel.format._util import _flatten_list + + +@dataclass +class SceneBuilder: + """Use this class for easily creating scenes for tests.""" + + result: Scene + + @classmethod + def empty(cls) -> SceneBuilder: + """Construct the SceneBuilder with an empty scene.""" + return SceneBuilder(Scene(metadata=Metadata(schema_version="1.0.0"))) + + def add_object( + self, + object_id: str | UUID | None = None, + object_type: str | None = None, + object_name: str | None = None, + ) -> SceneBuilder: + """Add an object to the scene.""" + scene = deepcopy(self.result) + + object_type, object_name = _resolve_empty_object_name_or_type(object_type, object_name) + object_id = _resolve_empty_object_uid(scene, object_id) + + scene.objects[object_id] = Object(object_name, object_type) + return SceneBuilder(scene) + + def add_sensor(self, sensor_id: str) -> SceneBuilder: + """Add a sensor to the scene. + + The sensor type is implicitly determined by the sensor_id. Id's, that start with 'rgb_' or + 'ir_' are added as a camera. If the id is 'lidar', it is a lidar. 'radar' creates a Radar. + 'gps_imu' creates a GpsImu. If the id does not match any of these, a OtherSensor is added. + """ + scene = deepcopy(self.result) + + truncated_sensor_id = sensor_id.split("_")[0].lower() + + if truncated_sensor_id in ["rgb", "ir"]: + scene.sensors[sensor_id] = Camera( + intrinsics=IntrinsicsPinhole( + camera_matrix=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + distortion=(0, 0, 0, 0, 0), + width_px=0, + height_px=0, + ) + ) + + elif truncated_sensor_id == "radar": + scene.sensors[sensor_id] = Radar( + intrinsics=IntrinsicsRadar(resolution_px_per_m=0, width_px=0, height_px=0) + ) + + elif truncated_sensor_id == "lidar": + scene.sensors[sensor_id] = Lidar() + + elif truncated_sensor_id == "gps": + scene.sensors[sensor_id] = GpsImu() + + else: + scene.sensors[sensor_id] = OtherSensor() + + return SceneBuilder(scene) + + def add_frame(self, frame_id: int | None = None, timestamp: float | None = None) -> SceneBuilder: + """Add a frame to the scene. + + If no frame_id is provided, the frame_id is the lowest number not currently occupied by + another frame_id. + + Example: + ```python + scene = SceneBuilder.empty().add_frame(frame_id=1).add_frame(frame_id=3).add_frame().result + assert sorted(list(scene.frames.keys())) == [1, 2, 3] + ``` + """ + scene = deepcopy(self.result) + + if frame_id is None: + frame_id = 1 + while frame_id in scene.frames: + frame_id += 1 + + scene.frames[frame_id] = Frame( + timestamp=Decimal(timestamp) if timestamp is not None else None + ) + + return SceneBuilder(scene) + + def add_annotation( + self, + annotation: Bbox | Cuboid | Poly2d | Poly3d | Seg3d, + uid: str | UUID | None = None, + frame_id: int = 1, + object_name: str = "person_0001", + sensor_id: str = "rgb_middle", + ) -> SceneBuilder: + """Add an annotation to the scene.""" + new_builder = deepcopy(self) + + uid = _resolve_empty_annotation_uid(new_builder.result, uid) + + if not _is_object_name_object(object_name, new_builder.result): + new_builder = new_builder.add_object(object_name=object_name) + + if sensor_id not in new_builder.result.sensors: + new_builder = new_builder.add_sensor(sensor_id) + + if frame_id not in new_builder.result.frames: + new_builder = new_builder.add_frame(frame_id=frame_id) + + annotation.object_id = _get_object_uid_from_name(object_name, new_builder.result) + annotation.sensor_id = sensor_id + + new_builder.result.frames[frame_id].annotations[uid] = annotation + return new_builder + + def add_bbox( + self, + uid: str | UUID | None = None, + frame_id: int = 1, + object_name: str = "person_0001", + sensor_id: str = "rgb_middle", + attributes: dict | None = None, + ) -> SceneBuilder: + """Add a bbox to the scene.""" + bbox = Bbox( + object_id=UUID("ffffffff-ffff-4fff-ffff-ffffffffffff"), + sensor_id=sensor_id, + pos=Point2d(0, 0), + size=Size2d(0, 0), + attributes=attributes if attributes is not None else {}, + ) + return self.add_annotation( + annotation=bbox, + uid=uid, + frame_id=frame_id, + object_name=object_name, + sensor_id=sensor_id, + ) + + def add_cuboid( + self, + uid: str | UUID | None = None, + frame_id: int = 1, + object_name: str = "person_0001", + sensor_id: str = "lidar", + attributes: dict | None = None, + ) -> SceneBuilder: + """Add a cuboid to the scene.""" + cuboid = Cuboid( + object_id=UUID("ffffffff-ffff-4fff-ffff-ffffffffffff"), + sensor_id=sensor_id, + pos=Point3d(0, 0, 0), + size=Size3d(0, 0, 0), + quat=Quaternion(0, 0, 0, 0), + attributes=attributes if attributes is not None else {}, + ) + return self.add_annotation( + annotation=cuboid, + uid=uid, + frame_id=frame_id, + object_name=object_name, + sensor_id=sensor_id, + ) + + def add_poly2d( + self, + uid: str | UUID | None = None, + frame_id: int = 1, + object_name: str = "person_0001", + sensor_id: str = "rgb_middle", + attributes: dict | None = None, + ) -> SceneBuilder: + """Add a poly2d to the scene.""" + poly2d = Poly2d( + object_id=UUID("ffffffff-ffff-4fff-ffff-ffffffffffff"), + sensor_id=sensor_id, + points=[], + closed=False, + attributes=attributes if attributes is not None else {}, + ) + return self.add_annotation( + annotation=poly2d, + uid=uid, + frame_id=frame_id, + object_name=object_name, + sensor_id=sensor_id, + ) + + def add_poly3d( + self, + uid: str | UUID | None = None, + frame_id: int = 1, + object_name: str = "person_0001", + sensor_id: str = "lidar", + attributes: dict | None = None, + ) -> SceneBuilder: + """Add a poly3d to the scene.""" + poly3d = Poly3d( + object_id=UUID("ffffffff-ffff-4fff-ffff-ffffffffffff"), + sensor_id=sensor_id, + points=[], + closed=False, + attributes=attributes if attributes is not None else {}, + ) + return self.add_annotation( + annotation=poly3d, + uid=uid, + frame_id=frame_id, + object_name=object_name, + sensor_id=sensor_id, + ) + + def add_seg3d( + self, + uid: str | UUID | None = None, + frame_id: int = 1, + object_name: str = "person_0001", + sensor_id: str = "lidar", + attributes: dict | None = None, + ) -> SceneBuilder: + """Add a poly3d to the scene.""" + seg3d = Seg3d( + object_id=UUID("ffffffff-ffff-4fff-ffff-ffffffffffff"), + sensor_id=sensor_id, + point_ids=[], + attributes=attributes if attributes is not None else {}, + ) + return self.add_annotation( + annotation=seg3d, + uid=uid, + frame_id=frame_id, + object_name=object_name, + sensor_id=sensor_id, + ) + + +def _resolve_empty_object_name_or_type( + object_type: str | None, object_name: str | None +) -> tuple[str, str]: + if object_name is None and object_type is None: + object_type = "person" + object_name = object_type + "_0000" + return object_type, object_name + + if object_name is None and object_type is not None: + object_name = object_type + "_0000" + return object_type, object_name + + if object_name is not None and object_type is None: + object_type = object_name.split("_")[0] + return object_type, object_name + + if object_name is not None and object_type is not None: + return object_type, object_name + + raise RuntimeError # this part is unreachable but this is the only way that mypy is happy + + +def _resolve_empty_object_uid(scene: Scene, object_id: str | UUID | None) -> UUID: + if object_id is None: + uid_index = 0 + while _generate_deterministic_uuid(uid_index, "5c59aad4") in scene.objects: + uid_index += 1 + + object_id = _generate_deterministic_uuid(uid_index, "5c59aad4") + + return UUID(str(object_id)) + + +def _generate_deterministic_uuid(index: int, prefix: str) -> UUID: + return UUID(f"{prefix.zfill(0)}-0000-4000-0000-{str(index).zfill(12)}") + + +def _resolve_empty_annotation_uid(scene: Scene, uid: str | UUID | None) -> UUID: + if uid is None: + annotation_uids_in_scene = set( + _flatten_list([tuple(frame.annotations.keys()) for frame in scene.frames.values()]) + ) + uid_index = 0 + while _generate_deterministic_uuid(uid_index, "6c95543d") in annotation_uids_in_scene: + uid_index += 1 + + uid = _generate_deterministic_uuid(uid_index, "6c95543d") + + return UUID(str(uid)) + + +def _is_object_name_object(object_name: str, scene: Scene) -> bool: + return any(object_.name == object_name for object_ in scene.objects.values()) + + +def _get_object_uid_from_name(object_name: str, scene: Scene) -> UUID: + for object_uid, object_ in scene.objects.items(): + if object_.name == object_name: + return object_uid + + raise RuntimeError diff --git a/raillabel/stats/__init__.py b/raillabel/stats/__init__.py deleted file mode 100644 index d6fb143..0000000 --- a/raillabel/stats/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 -"""Statsmodule provides statistics.""" - -from .generate_timespan import generate_timespan diff --git a/raillabel/stats/generate_timespan.py b/raillabel/stats/generate_timespan.py deleted file mode 100644 index 5847c31..0000000 --- a/raillabel/stats/generate_timespan.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import typing as t -from decimal import Decimal -from warnings import warn - -from typing_extensions import deprecated - -from ..format import Scene - - -@deprecated("This function will be removed in all future releases") -def generate_timespan(scene: Scene) -> t.Tuple[t.Optional[Decimal], t.Optional[Decimal]]: - """Return start and end timestamp of the scene. - - Parameters - ---------- - scene: raillabel.format.Scene - Scene the timespan should be based off. - - Returns - ------- - decimal.Decimal or None - Start timestamp of the scene. Is None if the scene has no frames. - decimal.Decimal or None - End timestamp of the scene. Is None if the scene has no frames. - """ - warn("This function will be removed in all future releases") - start_timestamp = None - end_timestamp = None - - for frame in scene.frames.values(): - - if start_timestamp == None or frame.timestamp < start_timestamp: - start_timestamp = frame.timestamp - - if end_timestamp == None or frame.timestamp > end_timestamp: - end_timestamp = frame.timestamp - - return (start_timestamp, end_timestamp) diff --git a/raillabel/validate/schemas/raillabel_schema.json b/raillabel/validate/schemas/raillabel_schema.json deleted file mode 100644 index 1365048..0000000 --- a/raillabel/validate/schemas/raillabel_schema.json +++ /dev/null @@ -1,937 +0,0 @@ -{ - "$id": "https://openlabel.asam.net/V1-0-0/schema#", - "$schema": "http://json-schema.org/draft-07/schema#", - "version": "3.0.0", - "description": "This schema is a sub-set of the OpenLABEL schema, that more accurately describes the usage by raillabel. Every JSON file, that is valid in this schema, is also valid in the OpenLABEL schema.", - "additionalProperties": false, - "definitions": { - "attributes": { - "additionalProperties": false, - "description": "Attributes is the alias of element data that can be nested inside geometric object data. For example, a certain bounding box can have attributes related to its score, visibility, etc. These values can be nested inside the bounding box as attributes.", - "properties": { - "boolean": { - "items": { - "$ref": "#/definitions/boolean_attribute" - }, - "type": "array" - }, - "num": { - "items": { - "$ref": "#/definitions/num_attribute" - }, - "type": "array" - }, - "text": { - "items": { - "$ref": "#/definitions/text_attribute" - }, - "type": "array" - }, - "vec": { - "items": { - "$ref": "#/definitions/vec_attribute" - }, - "type": "array" - } - }, - "type": "object" - }, - "bbox": { - "additionalProperties": false, - "description": "A 2D bounding box is defined as a 4-dimensional vector [x, y, w, h], where [x, y] is the center of the bounding box and [w, h] represent the width (horizontal, x-coordinate dimension) and height (vertical, y-coordinate dimension), respectively.", - "properties": { - "attributes": { - "$ref": "#/definitions/attributes" - }, - "coordinate_system": { - "description": "Name of the coordinate system in respect of which this object data is expressed.", - "type": "string" - }, - "name": { - "description": "This is a string encoding the name of this object data. It is used as index inside the corresponding object data pointers.", - "type": "string" - }, - "val": { - "description": "The array of 4 values that define the [x, y, w, h] values of the bbox.", - "items": { - "type": "number" - }, - "maxItems": 4, - "minItems": 4, - "type": "array" - }, - "uid": { - "description": "This is a string encoding the Universal Unique identifyer of the annotation.", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "name", - "val" - ], - "type": "object" - }, - "boolean_attribute": { - "additionalProperties": false, - "description": "A boolean attribute.", - "properties": { - "name": { - "description": "Friendly identifier describing the attribute. Used to track the attribute throughout annotations and frames.", - "type": "string" - }, - "val": { - "description": "The boolean value.", - "type": "boolean" - } - }, - "required": [ - "val" - ], - "type": "object" - }, - "coordinate_system": { - "additionalProperties": false, - "description": "A coordinate system is a 3D reference frame. Spatial information on objects and their properties can be defined with respect to coordinate systems.", - "properties": { - "children": { - "description": "List of children of this coordinate system.", - "items": { - "description": "This is the string UID of this child coordinate system.", - "type": "string" - }, - "type": "array" - }, - "parent": { - "description": "This is the string UID of the parent coordinate system this coordinate system is referring to.", - "type": "string", - "enum": [ - "base", - "" - ] - }, - "pose_wrt_parent": { - "$ref": "#/definitions/transform_data" - }, - "type": { - "description": "This is a string that describes the type of the coordinate system, for example, \"local\", \"geo\").", - "type": "string", - "enum": [ - "sensor", - "local" - ] - } - }, - "required": [ - "type", - "parent" - ] - }, - "coordinate_systems": { - "additionalProperties": false, - "description": "This is a JSON object which contains OpenLABEL coordinate systems. Coordinate system keys can be any string, for example, a friendly coordinate system name.", - "patternProperties": { - "^": { - "$ref": "#/definitions/coordinate_system" - } - }, - "type": "object" - }, - "cuboid": { - "additionalProperties": false, - "description": "A cuboid or 3D bounding box. It is defined by the position of its center, the rotation in 3D, and its dimensions.", - "properties": { - "attributes": { - "$ref": "#/definitions/attributes" - }, - "coordinate_system": { - "description": "Name of the coordinate system in respect of which this object data is expressed.", - "type": "string" - }, - "name": { - "description": "This is a string encoding the name of this object data. It is used as index inside the corresponding object data pointers.", - "type": "string" - }, - "val": { - "description": "List of values encoding the position, rotation and dimensions. Two options are supported, using 9 or 10 values. If 9 values are used, the format is (x, y, z, rx, ry, rz, sx, sy, sz), where (x, y, z) encodes the position, (rx, ry, rz) encodes the Euler angles that encode the rotation, and (sx, sy, sz) are the dimensions of the cuboid in its object coordinate system. If 10 values are used, then the format is (x, y, z, qx, qy, qz, qw, sx, sy, sz) with the only difference of the rotation values which are the 4 values of a quaternion.", - "oneOf": [ - { - "items": { - "type": "number" - }, - "maxItems": 10, - "minItems": 9, - "type": "array" - }, - { - "type": "null" - } - ] - }, - "uid": { - "description": "This is a string encoding the Universal Unique identifyer of the annotation.", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "name", - "val" - ], - "type": "object" - }, - "element_data_pointer": { - "description": "This item contains pointers to element data of elements, indexed by \"name\", and containing information about the element data type, for example, bounding box, cuboid, and the frame intervals in which this element_data exists within an element. That means, these pointers can be used to explore element data dynamic information within the JSON content.", - "properties": { - "attribute_pointers": { - "description": "This is a JSON object which contains pointers to the attributes of the element data pointed by this pointer. The attributes pointer keys shall be the \"name\" of the attribute of the element data this pointer points to.", - "patternProperties": { - "^": { - "description": "The attribute pointer values are strings which define the type of the attribute.", - "enum": [ - "num", - "text", - "boolean", - "vec" - ], - "type": "string" - } - }, - "type": "object" - }, - "frame_intervals": { - "description": "List of frame intervals of the element data pointed by this pointer.", - "items": { - "$ref": "#/definitions/frame_interval" - }, - "type": "array" - }, - "type": { - "description": "Type of the element data pointed by this pointer.", - "enum": [ - "bbox", - "num", - "poly2d", - "poly3d", - "cuboid", - "vec" - ], - "type": "string" - } - }, - "required": [ - "attribute_pointers", - "frame_intervals", - "type" - ], - "additionalProperties": false - }, - "element_data_pointers": { - "additionalProperties": false, - "description": "This is a JSON object which contains OpenLABEL element data pointers. Element data pointer keys shall be the \"name\" of the element data this pointer points to.", - "patternProperties": { - "^": { - "$ref": "#/definitions/element_data_pointer" - } - }, - "type": "object" - }, - "frame": { - "additionalProperties": false, - "description": "A frame is a container of dynamic, timewise, information.", - "properties": { - "frame_properties": { - "additionalProperties": false, - "description": "This is a JSON object which contains information about this frame.", - "properties": { - "streams": { - "additionalProperties": false, - "description": "Streams is a JSON object which contains OpenLABEL streams with specific information for this frame. Stream keys can be any string, for example, a friendly stream name.", - "patternProperties": { - "^": { - "$ref": "#/definitions/stream_sync" - } - }, - "type": "object" - }, - "timestamp": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ], - "description": "The timestamp indicates a time instant as a UTC string or numerical value to describe this frame." - }, - "frame_data": { - "additionalProperties": false, - "description": "Additional data to describe attributes of the frame.", - "properties": { - "num": { - "description": "List of \"num\" that describe this frame.", - "items": { - "$ref": "#/definitions/num" - }, - "type": "array" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "objects": { - "additionalProperties": false, - "description": "This is a JSON object that contains dynamic information on OpenLABEL objects. Object keys are strings containing numerical UIDs or 32 bytes UUIDs. Object values may contain an \"object_data\" JSON object.", - "patternProperties": { - "^(-?[0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$": { - "additionalProperties": false, - "properties": { - "object_data": { - "$ref": "#/definitions/object_data" - } - }, - "type": "object" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "frame_interval": { - "additionalProperties": false, - "description": "A frame interval defines a starting and ending frame number as a closed interval. That means the interval includes the limit frame numbers.", - "properties": { - "frame_end": { - "description": "Ending frame number of the interval.", - "type": "integer" - }, - "frame_start": { - "description": "Initial frame number of the interval.", - "type": "integer" - } - }, - "type": "object" - }, - "metadata": { - "additionalProperties": true, - "description": "This JSON object contains information, that is, metadata, about the annotation file itself.", - "properties": { - "comment": { - "description": "Additional information or description about the annotation content.", - "type": "string" - }, - "file_version": { - "description": "Version number of the OpenLABEL annotation content.", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$", - "type": "string" - }, - "name": { - "description": "Name of the OpenLABEL annotation content.", - "type": "string" - }, - "schema_version": { - "description": "Version number of the OpenLABEL schema this annotation JSON object follows.", - "enum": [ - "1.0.0" - ], - "type": "string" - }, - "tagged_file": { - "description": "File name or URI of the data file being tagged.", - "type": "string" - }, - "exporter_version": { - "description": "Version number of the exporter software.", - "type": "string" - }, - "subschema_version": { - "description": "Version number of the RailLabel subschema this annotation JSON object follows.", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$", - "type": "string" - } - }, - "required": [ - "schema_version" - ], - "type": "object" - }, - "num": { - "additionalProperties": false, - "description": "A number.", - "properties": { - "coordinate_system": { - "description": "Name of the coordinate system in respect of which this object data is expressed.", - "type": "string" - }, - "name": { - "description": "This is a string encoding the name of this object data.", - "type": "string" - }, - "val": { - "description": "The numerical value of the number.", - "type": "number" - }, - "uid": { - "description": "This is a string encoding the Universal Unique identifyer of the annotation.", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "name", - "val" - ], - "type": "object" - }, - "num_attribute": { - "additionalProperties": false, - "description": "A number attribute.", - "properties": { - "name": { - "description": "Friendly identifier describing the attribute. Used to track the attribute throughout annotations and frames.", - "type": "string" - }, - "val": { - "description": "The numerical value of the number.", - "type": "number" - } - }, - "required": [ - "val" - ], - "type": "object" - }, - "object": { - "additionalProperties": false, - "description": "An object is the main type of annotation element. Object is designed to represent spatiotemporal entities, such as physical objects in the real world. Objects shall have a name and type. Objects may have static and dynamic data. Objects are the only type of elements that may have geometric data, such as bounding boxes, cuboids, polylines, images, etc.", - "properties": { - "frame_intervals": { - "description": "The array of frame intervals where this object exists or is defined.", - "items": { - "$ref": "#/definitions/frame_interval" - }, - "type": "array" - }, - "name": { - "description": "Name of the object. It is a friendly name and not used for indexing.", - "type": "string" - }, - "object_data_pointers": { - "$ref": "#/definitions/element_data_pointers" - }, - "type": { - "description": "The type of an object defines the class the object corresponds to.", - "type": "string" - } - }, - "required": [ - "name", - "type" - ], - "type": "object" - }, - "object_data": { - "additionalProperties": false, - "description": "Additional data to describe attributes of the object.", - "properties": { - "bbox": { - "description": "List of \"bbox\" that describe this object.", - "items": { - "$ref": "#/definitions/bbox" - }, - "type": "array" - }, - "cuboid": { - "description": "List of \"cuboid\" that describe this object.", - "items": { - "$ref": "#/definitions/cuboid" - }, - "type": "array" - }, - "poly2d": { - "description": "List of \"poly2d\" that describe this object.", - "items": { - "$ref": "#/definitions/poly2d" - }, - "type": "array" - }, - "poly3d": { - "description": "List of \"poly3d\" that describe this object.", - "items": { - "$ref": "#/definitions/poly3d" - }, - "type": "array" - }, - "vec": { - "description": "List of \"vec\" that describe this object.", - "items": { - "$ref": "#/definitions/vec" - }, - "type": "array" - } - }, - "type": "object" - }, - "openlabel": { - "additionalProperties": false, - "description": "The OpenLABEL root JSON object, which contains all other JSON objects.", - "properties": { - "coordinate_systems": { - "$ref": "#/definitions/coordinate_systems" - }, - "frame_intervals": { - "description": "This is an array of frame intervals.", - "item": { - "$ref": "#/definitions/frame_interval" - }, - "type": "array" - }, - "frames": { - "additionalProperties": false, - "description": "This is the JSON object of frames that contain the dynamic, timewise, annotations. Keys are strings containing numerical frame identifiers, which are denoted as master frame numbers.", - "patternProperties": { - "^[0-9]+$": { - "$ref": "#/definitions/frame" - } - }, - "type": "object" - }, - "metadata": { - "$ref": "#/definitions/metadata" - }, - "objects": { - "additionalProperties": false, - "description": "This is the JSON object of OpenLABEL objects. Object keys are strings containing numerical UIDs or 32 bytes UUIDs.", - "patternProperties": { - "^(-?[0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$": { - "$ref": "#/definitions/object" - } - }, - "type": "object" - }, - "streams": { - "$ref": "#/definitions/streams" - } - }, - "required": [ - "metadata" - ], - "type": "object" - }, - "poly2d": { - "additionalProperties": false, - "description": "A 2D polyline defined as a sequence of 2D points.", - "properties": { - "attributes": { - "$ref": "#/definitions/attributes" - }, - "closed": { - "description": "A boolean that defines whether the polyline is closed or not. In case it is closed, it is assumed that the last point of the sequence is connected with the first one.", - "type": "boolean" - }, - "coordinate_system": { - "description": "Name of the coordinate system in respect of which this object data is expressed.", - "type": "string" - }, - "mode": { - "description": "Mode of the polyline list of values: \"MODE_POLY2D_ABSOLUTE\" determines that the poly2d list contains the sequence of (x, y) values of all points of the polyline. \"MODE_POLY2D_RELATIVE\" specifies that only the first point of the sequence is defined with its (x, y) values, while all the rest are defined relative to it. \"MODE_POLY2D_SRF6DCC\" specifies that SRF6DCC chain code method is used. \"MODE_POLY2D_RS6FCC\" specifies that the RS6FCC method is used.", - "type": "string" - }, - "name": { - "description": "This is a string encoding the name of this object data. It is used as index inside the corresponding object data pointers.", - "type": "string" - }, - "val": { - "description": "List of numerical values of the polyline, according to its mode.", - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "items": { - "type": "number" - }, - "type": "array" - } - ] - }, - "uid": { - "description": "This is a string encoding the Universal Unique identifyer of the annotation.", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "name", - "val", - "mode", - "closed" - ], - "type": "object" - }, - "poly3d": { - "additionalProperties": false, - "description": "A 3D polyline defined as a sequence of 3D points.", - "properties": { - "attributes": { - "$ref": "#/definitions/attributes" - }, - "closed": { - "type": "boolean", - "description": "A boolean that defines whether the polyline is closed or not. In case it is closed, it is assumed that the last point of the sequence is connected with the first one." - }, - "coordinate_system": { - "description": "Name of the coordinate system in respect of which this object data is expressed.", - "type": "string" - }, - "name": { - "description": "This is a string encoding the name of this object data. It is used as index inside the corresponding object data pointers.", - "type": "string" - }, - "val": { - "description": "List of numerical values of the polyline, according to its mode.", - "items": { - "type": "number" - }, - "type": "array" - }, - "uid": { - "description": "This is a string encoding the Universal Unique identifyer of the annotation.", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "name", - "val", - "closed" - ], - "type": "object" - }, - "stream_camera": { - "additionalProperties": false, - "description": "A stream describes the source of a data sequence, usually a sensor. This specific object contains the intrinsics of a camera sensor.", - "properties": { - "description": { - "description": "Description of the stream.", - "type": "string" - }, - "stream_properties": { - "additionalProperties": false, - "description": "Additional properties of the stream.", - "properties": { - "intrinsics_pinhole": { - "additionalProperties": false, - "description": "This JSON object defines an instance of the intrinsic parameters of a pinhole camera.", - "properties": { - "camera_matrix": { - "description": "This is a 3x4 camera matrix which projects 3D homogeneous points (4x1) from a camera coordinate system into the image plane (3x1). This is the usual K matrix for camera projection as in OpenCV. It is extended from 3x3 to 3x4 to enable its direct utilisation to project 4x1 homogeneous 3D points. The matrix is defined to follow the camera model: x-to-right, y-down, z-forward. The following equation applies: x_img = camera_matrix * X_ccs.", - "items": { - "type": "number" - }, - "maxItems": 12, - "minItems": 12, - "type": "array" - }, - "distortion_coeffs": { - "description": "This is the array 1xN radial and tangential distortion coefficients.", - "items": { - "type": "number" - }, - "maxItems": 14, - "minItems": 5, - "type": "array" - }, - "height_px": { - "type": "integer" - }, - "width_px": { - "type": "integer" - } - }, - "type": "object", - "required": [ - "camera_matrix", - "distortion_coeffs", - "height_px", - "width_px" - ] - } - } - }, - "type": { - "description": "A string encoding the type of the stream.", - "enum": [ - "camera" - ], - "type": "string" - }, - "uri": { - "description": "A string encoding the subdirectory containing the sensor files.", - "type": "string" - } - }, - "type": "object" - }, - "stream_radar": { - "additionalProperties": false, - "description": "A stream describes the source of a data sequence, usually a sensor. This specific object contains the intrinsics of a camera sensor.", - "properties": { - "description": { - "description": "Description of the stream.", - "type": "string" - }, - "stream_properties": { - "additionalProperties": false, - "description": "Additional properties of the stream.", - "properties": { - "intrinsics_radar": { - "additionalProperties": false, - "description": "This JSON object defines an instance of the intrinsic parameters of a radar.", - "properties": { - "resolution_px_per_m": { - "type": "number" - }, - "height_px": { - "type": "integer" - }, - "width_px": { - "type": "integer" - } - }, - "type": "object", - "required": [ - "resolution_px_per_m", - "height_px", - "width_px" - ] - } - } - }, - "type": { - "description": "A string encoding the type of the stream.", - "enum": [ - "radar" - ], - "type": "string" - }, - "uri": { - "description": "A string encoding the subdirectory containing the sensor files.", - "type": "string" - } - }, - "type": "object" - }, - "stream_other": { - "additionalProperties": false, - "description": "A stream describes the source of a data sequence, usually a sensor. This specific object is used for streams without intrinsic calibration.", - "properties": { - "description": { - "description": "Description of the stream.", - "type": "string" - }, - "type": { - "description": "A string encoding the type of the stream.", - "enum": [ - "lidar", - "gps_imu", - "other" - ], - "type": "string" - }, - "uri": { - "description": "A string encoding the subdirectory containing the sensor files.", - "type": "string" - } - }, - "type": "object" - }, - "stream_sync": { - "additionalProperties": false, - "description": "Syncronization information of a stream in a frame.", - "properties": { - "stream_properties": { - "additionalProperties": false, - "properties": { - "sync": { - "additionalProperties": false, - "properties": { - "timestamp": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ], - "description": "The timestamp indicates a time instant as a UTC string or numerical value to describe this frame." - } - }, - "type": "object" - } - }, - "type": "object" - }, - "uri": { - "description": "A string encoding the URI, for example, a URL, or file name, for example, a video file name, the stream corresponds to.", - "type": "string" - } - }, - "type": "object" - }, - "streams": { - "additionalProperties": false, - "description": "This is a JSON object which contains OpenLABEL streams. Stream keys can be any string, for example, a friendly stream name.", - "patternProperties": { - "^": { - "oneOf": [ - { "$ref": "#/definitions/stream_camera" }, - { "$ref": "#/definitions/stream_radar" }, - { "$ref": "#/definitions/stream_other" } - ] - } - }, - "type": "object" - }, - "text_attribute": { - "additionalProperties": false, - "description": "A text attribute.", - "properties": { - "name": { - "description": "Friendly identifier describing the attribute. Used to track the attribute throughout annotations and frames.", - "type": "string" - }, - "val": { - "description": "The characters of the text.", - "type": "string" - } - }, - "required": [ - "val" - ], - "type": "object" - }, - "transform_data": { - "additionalProperties": false, - "description": "A transform can be defined with a quaternion to encode the rotation of a coordinate system with respect to another, and a translation.", - "properties": { - "quaternion": { - "description": "List of 4 values encoding a quaternion (x, y, z, w).", - "items": { - "type": "number" - }, - "maxItems": 4, - "minItems": 4, - "type": "array" - }, - "translation": { - "description": "List of 3 values encoding the translation vector (x, y, z)", - "items": { - "type": "number" - }, - "maxItems": 3, - "minItems": 3, - "type": "array" - } - }, - "required": [ - "quaternion", - "translation" - ], - "type": "object" - }, - "vec": { - "additionalProperties": false, - "description": "A vector (list) of numbers or strings.", - "properties": { - "attributes": { - "$ref": "#/definitions/attributes" - }, - "coordinate_system": { - "description": "Name of the coordinate system in respect of which this object data is expressed.", - "type": "string" - }, - "name": { - "description": "This is a string encoding the name of this object data. It is used as index inside the corresponding object data pointers.", - "type": "string" - }, - "type": { - "description": "This attribute specifies whether the vector shall be considered as a descriptor of individual values or as a definition of a range.", - "enum": [ - "values", - "range" - ], - "type": "string" - }, - "val": { - "description": "The numerical values of the vector (list) of numbers.", - "items": { - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - }, - "type": "array" - }, - "uid": { - "description": "This is a string encoding the Universal Unique identifyer of the annotation.", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "val" - ], - "type": "object" - }, - "vec_attribute": { - "additionalProperties": false, - "description": "A vector attribute.", - "properties": { - "name": { - "description": "Friendly identifier describing the attribute. Used to track the attribute throughout annotations and frames.", - "type": "string" - }, - "val": { - "description": "The numerical values of the vector (list) of numbers.", - "items": { - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - }, - "type": "array" - } - }, - "required": [ - "val" - ], - "type": "object" - } - }, - "properties": { - "openlabel": { - "$ref": "#/definitions/openlabel" - } - }, - "required": [ - "openlabel" - ], - "type": "object" -} diff --git a/raillabel/validate/schemas/raillabel_schema.json.license b/raillabel/validate/schemas/raillabel_schema.json.license deleted file mode 100644 index fbb8ddf..0000000 --- a/raillabel/validate/schemas/raillabel_schema.json.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: Copyright DB InfraGO AG and the raillabel contributors -SPDX-License-Identifier: Apache-2.0 diff --git a/raillabel/validate/schemas/understand_ai_t4_schema.json b/raillabel/validate/schemas/understand_ai_t4_schema.json deleted file mode 100644 index eeeba35..0000000 --- a/raillabel/validate/schemas/understand_ai_t4_schema.json +++ /dev/null @@ -1,604 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "definitions": { - "2D_BOUNDING_BOX": { - "type": "array", - "items": { - "description": "tbd", - "additionalProperties": false, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "objectId": { - "type": "string" - }, - "className": { - "type": "string" - }, - "geometry": { - "additionalProperties": false, - "type": "object", - "properties": { - "xMin": { - "type": "number" - }, - "yMin": { - "type": "number" - }, - "xMax": { - "type": "number" - }, - "yMax": { - "type": "number" - } - }, - "required": [ - "xMax", - "xMin", - "yMax", - "yMin" - ] - }, - "attributes": { - "$ref": "#/definitions/attributes" - }, - "sensor": { - "$ref": "#/definitions/sensor" - } - }, - "required": [ - "attributes", - "className", - "geometry", - "id", - "objectId", - "sensor" - ] - } - }, - "2D_POLYGON": { - "type": "array", - "items": { - "description": "tbd", - "additionalProperties": false, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "objectId": { - "type": "string" - }, - "className": { - "type": "string" - }, - "geometry": { - "additionalProperties": false, - "type": "object", - "properties": { - "points": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 2 - } - } - }, - "required": [ - "points" - ] - }, - "attributes": { - "$ref": "#/definitions/attributes" - }, - "sensor": { - "$ref": "#/definitions/sensor" - } - }, - "required": [ - "attributes", - "className", - "geometry", - "id", - "objectId", - "sensor" - ] - } - }, - "2D_POLYLINE": { - "type": "array", - "items": { - "description": "tbd", - "additionalProperties": false, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "objectId": { - "type": "string" - }, - "className": { - "type": "string" - }, - "geometry": { - "additionalProperties": false, - "type": "object", - "properties": { - "points": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 2 - } - } - }, - "required": [ - "points" - ] - }, - "attributes": { - "$ref": "#/definitions/attributes" - }, - "sensor": { - "$ref": "#/definitions/sensor" - } - }, - "required": [ - "attributes", - "className", - "geometry", - "id", - "objectId", - "sensor" - ] - } - }, - "3D_BOUNDING_BOX": { - "type": "array", - "items": { - "description": "tbd", - "additionalProperties": false, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "objectId": { - "type": "string" - }, - "className": { - "type": "string" - }, - "geometry": { - "additionalProperties": false, - "type": "object", - "properties": { - "size": { - "additionalProperties": false, - "type": "object", - "properties": { - "height": { - "type": "number" - }, - "width": { - "type": "number" - }, - "length": { - "type": "number" - } - }, - "required": [ - "height", - "length", - "width" - ] - }, - "center": { - "additionalProperties": false, - "type": "object", - "properties": { - "x": { - "type": "number" - }, - "y": { - "type": "number" - }, - "z": { - "type": "number" - } - }, - "required": [ - "x", - "y", - "z" - ] - }, - "quaternion": { - "additionalProperties": false, - "type": "object", - "properties": { - "x": { - "type": "number" - }, - "y": { - "type": "number" - }, - "z": { - "type": "number" - }, - "w": { - "type": "number" - } - }, - "required": [ - "w", - "x", - "y", - "z" - ] - } - }, - "required": [ - "center", - "quaternion", - "size" - ] - }, - "attributes": { - "$ref": "#/definitions/attributes" - }, - "sensor": { - "$ref": "#/definitions/sensor" - } - }, - "required": [ - "attributes", - "className", - "geometry", - "id", - "objectId", - "sensor" - ] - } - }, - "3D_SEGMENTATION": { - "type": "array", - "items": { - "additionalProperties": false, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "objectId": { - "type": "string" - }, - "className": { - "type": "string" - }, - "geometry": { - "additionalProperties": false, - "type": "object", - "properties": { - "associatedPoints": { - "additionalProperties": false, - "type": "array", - "items": { - "type": "integer" - } - }, - "numberOfPointsInBox": { - "type": "integer" - } - }, - "required": [ - "associatedPoints", - "numberOfPointsInBox" - ] - }, - "attributes": { - "$ref": "#/definitions/attributes" - }, - "sensor": { - "$ref": "#/definitions/sensor" - } - }, - "required": [ - "attributes", - "className", - "geometry", - "id", - "objectId", - "sensor" - ] - } - }, - "attributes": { - "description": "tbd", - "additionalProperties": false, - "type": "object", - "patternProperties": { - "^": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "array", - "items": { - "type": "number" - } - } - ] - } - } - }, - "coordinateSystems": { - "description": "tbd", - "type": "array", - "items": { - "additionalProperties": false, - "type": "object", - "properties": { - "coordinate_system_id": { - "type": "string" - }, - "topic": { - "description": "tbd", - "type": "string" - }, - "frame_id": { - "description": "tbd", - "type": "string" - }, - "position": { - "description": "tbd", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 3, - "maxItems": 3 - }, - "rotation_quaternion": { - "description": "tbd", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 4, - "maxItems": 4 - }, - "rotation_matrix": { - "description": "tbd", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 9, - "maxItems": 9 - }, - "angle_axis_rotation": { - "description": "tbd", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 3, - "maxItems": 3 - }, - "homogeneous_transform": { - "description": "tbd", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 16, - "maxItems": 16 - }, - "measured_position": { - "description": "tbd", - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 3, - "maxItems": 3 - }, - "camera_matrix": { - "description": "tbd", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 9, - "maxItems": 9 - }, - "dist_coeffs": { - "description": "tbd", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 5, - "maxItems": 5 - }, - "rotation_around_z_in_degrees": { - "description": "tbd", - "type": "number" - } - }, - "required": [ - "angle_axis_rotation", - "coordinate_system_id", - "frame_id", - "position", - "rotation_matrix", - "rotation_quaternion", - "topic" - ] - } - }, - "frames": { - "type": "array", - "items": { - "description": "tbd", - "additionalProperties": false, - "type": "object", - "properties": { - "frameId": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "annotations": { - "description": "tbd", - "additionalProperties": false, - "type": "object", - "properties": { - "2D_BOUNDING_BOX": { - "$ref": "#/definitions/2D_BOUNDING_BOX" - }, - "2D_POLYLINE": { - "$ref": "#/definitions/2D_POLYLINE" - }, - "2D_POLYGON": { - "$ref": "#/definitions/2D_POLYGON" - }, - "3D_BOUNDING_BOX": { - "$ref": "#/definitions/3D_BOUNDING_BOX" - }, - "3D_SEGMENTATION": { - "$ref": "#/definitions/3D_SEGMENTATION" - } - }, - "required": [ - "2D_BOUNDING_BOX", - "2D_POLYGON", - "2D_POLYLINE", - "3D_BOUNDING_BOX", - "3D_SEGMENTATION" - ] - } - }, - "required": [ - "annotations", - "frameId", - "timestamp" - ] - } - }, - "metadata": { - "additionalProperties": false, - "type": "object", - "properties": { - "clip_id": { - "description": "tbd", - "type": "string" - }, - "external_clip_id": { - "description": "tbd", - "type": "string" - }, - "project_id": { - "description": "tbd", - "type": "string" - }, - "export_time": { - "type": "string", - "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2} UTC$" - }, - "exporter_version": { - "type": "string" - }, - "coordinate_system_3d": { - "description": "tbd", - "type": "string" - }, - "coordinate_system_reference": { - "description": "tbd", - "type": "string" - }, - "folder_name": { - "description": "tbd", - "type": "string" - } - }, - "required": [ - "clip_id", - "coordinate_system_3d", - "coordinate_system_reference", - "export_time", - "exporter_version", - "external_clip_id", - "folder_name", - "project_id" - ] - }, - "sensor": { - "additionalProperties": false, - "type": "object", - "properties": { - "type": { - "description": "tbd", - "type": "string" - }, - "uri": { - "description": "tbd", - "type": "string" - }, - "timestamp": { - "description": "tbd", - "type": "string" - } - }, - "required": [ - "timestamp", - "type", - "uri" - ] - } - }, - "properties": { - "metadata": { - "$ref": "#/definitions/metadata" - }, - "coordinateSystems": { - "$ref": "#/definitions/coordinateSystems" - }, - "frames": { - "$ref": "#/definitions/frames" - } - }, - "required": [ - "coordinateSystems", - "frames", - "metadata" - ] -} diff --git a/raillabel/validate/schemas/understand_ai_t4_schema.json.license b/raillabel/validate/schemas/understand_ai_t4_schema.json.license deleted file mode 100644 index fbb8ddf..0000000 --- a/raillabel/validate/schemas/understand_ai_t4_schema.json.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: Copyright DB InfraGO AG and the raillabel contributors -SPDX-License-Identifier: Apache-2.0 diff --git a/raillabel/validate/validate.py b/raillabel/validate/validate.py deleted file mode 100644 index 55c01cf..0000000 --- a/raillabel/validate/validate.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import json -import os -import typing as t -from pathlib import Path -from warnings import warn - -import fastjsonschema -import jsonschema -from typing_extensions import deprecated - -from .. import exceptions - - -@deprecated("This function will be moved to `raillabel_providerkit` in all future releases.") -def validate(data: dict, schema_path: str = "raillabel") -> t.Tuple[bool, t.List[str]]: - """Validate JSON data represented by a dict via a given schema. - - Parameters - ---------- - data: dict - JSON data to be validated. - schema_path: str, optional - Path to the JSON schema used for the validation. If the schema is in the /schemas - folder, the format name can be used (i.e. schema_path can be 'raillabel' or - 'raillabel_schema' to load the raillabel_schema.json file). Default is - 'raillabel'. - - Returns - ------- - is_data_valid: bool - True if the data validates against the schema, False if not. - schema_errors: t.List of str - All SchemaError messages found in the data. If the data is valid (if no SchemaErrors are - found), this is an empty t.List. - """ - - # Since the schema_path can either be a complete path or the short name of a schema, these two - # options must be distinguished. It is therefore assumed, that a complete path contains at # - # least one '/' or '\'. - - warn("This function will be moved to `raillabel_providerkit` in all future releases.") - - if "/" in schema_path or "\\" in schema_path: # if schema_path is a complete path - schema_path = Path(schema_path) - - else: # if schema_path is a schema name in /schemas - local_schemas = [ # t.List of json files in /schemas - p for p in os.listdir(Path(__file__).parent / "schemas") if p.endswith(".json") - ] - - applicable_local_schemas = [] # t.List of possible schemas - for local_schema_path in local_schemas: - if schema_path in local_schema_path: - applicable_local_schemas.append(local_schema_path) - - if len(applicable_local_schemas) == 1: # if exactly one applicable file has been found - schema_path = Path(__file__).parent / "schemas" / applicable_local_schemas[0] - - elif len(applicable_local_schemas) == 0: # if no applicable files have been found - - err_msg = f"The key {schema_path} does not apply to a schema. Available schema files:" - for p in local_schemas: - err_msg += "\n - " + p - - raise FileNotFoundError(err_msg) - - else: # if more than one applicable files have been found - - err_msg = f"The key {schema_path} applies to multiple files in /schemas:" - for p in applicable_local_schemas: - err_msg += "\n - " + p - - raise exceptions.AmbiguousSchemaNameError(err_msg) - - # Loads the schema data - try: - with schema_path.open() as schema_file: - schema = json.load(schema_file) - - except FileNotFoundError as e: - raise FileNotFoundError(f"The schema file could not be found in {schema_path}") from e - - # Validates the data - schema_errors = [] - - try: - # Use fastjsonschema since its faster (duh), and use jsonschema as a fallback if we find - # an error to maintain jsonschemas way of reporting all errors - fastjsonschema.validate(schema, data) - except fastjsonschema.JsonSchemaException as _: - validator = jsonschema.Draft7Validator(schema=schema) - - for error in validator.iter_errors(data): - schema_errors.append("$" + error.json_path[1:] + ": " + str(error.message)) - - is_data_valid = len(schema_errors) == 0 - - return is_data_valid, schema_errors diff --git a/tests/__test_assets__/metaschema.json b/tests/__test_assets__/metaschema.json deleted file mode 100644 index fb92c7f..0000000 --- a/tests/__test_assets__/metaschema.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://json-schema.org/draft-07/schema#", - "title": "Core schema meta-schema", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$ref": "#" } - }, - "nonNegativeInteger": { - "type": "integer", - "minimum": 0 - }, - "nonNegativeIntegerDefault0": { - "allOf": [ - { "$ref": "#/definitions/nonNegativeInteger" }, - { "default": 0 } - ] - }, - "simpleTypes": { - "enum": [ - "array", - "boolean", - "integer", - "null", - "number", - "object", - "string" - ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true, - "default": [] - } - }, - "type": ["object", "boolean"], - "properties": { - "$id": { - "type": "string", - "format": "uri-reference" - }, - "$schema": { - "type": "string", - "format": "uri" - }, - "$ref": { - "type": "string", - "format": "uri-reference" - }, - "$comment": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": true, - "readOnly": { - "type": "boolean", - "default": false - }, - "writeOnly": { - "type": "boolean", - "default": false - }, - "examples": { - "type": "array", - "items": true - }, - "multipleOf": { - "type": "number", - "exclusiveMinimum": 0 - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "number" - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "number" - }, - "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, - "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "additionalItems": { "$ref": "#" }, - "items": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/schemaArray" } - ], - "default": true - }, - "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, - "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "contains": { "$ref": "#" }, - "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, - "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "required": { "$ref": "#/definitions/stringArray" }, - "additionalProperties": { "$ref": "#" }, - "definitions": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "propertyNames": { "format": "regex" }, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/stringArray" } - ] - } - }, - "propertyNames": { "$ref": "#" }, - "const": true, - "enum": { - "type": "array", - "items": true, - "minItems": 1, - "uniqueItems": true - }, - "type": { - "anyOf": [ - { "$ref": "#/definitions/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/definitions/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "format": { "type": "string" }, - "contentMediaType": { "type": "string" }, - "contentEncoding": { "type": "string" }, - "if": { "$ref": "#" }, - "then": { "$ref": "#" }, - "else": { "$ref": "#" }, - "allOf": { "$ref": "#/definitions/schemaArray" }, - "anyOf": { "$ref": "#/definitions/schemaArray" }, - "oneOf": { "$ref": "#/definitions/schemaArray" }, - "not": { "$ref": "#" } - }, - "default": true -} diff --git a/tests/__test_assets__/metaschema.json.license b/tests/__test_assets__/metaschema.json.license deleted file mode 100644 index fbb8ddf..0000000 --- a/tests/__test_assets__/metaschema.json.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: Copyright DB InfraGO AG and the raillabel contributors -SPDX-License-Identifier: Apache-2.0 diff --git a/tests/__test_assets__/openlabel_v1_schema.json b/tests/__test_assets__/openlabel_v1_schema.json index 45997a6..8d0021e 100644 --- a/tests/__test_assets__/openlabel_v1_schema.json +++ b/tests/__test_assets__/openlabel_v1_schema.json @@ -24,12 +24,12 @@ "description": "Name of the action. It is a friendly name and not used for indexing.", "type": "string" }, - "ontology_uid": { + "ontology_id": { "description": "This is the UID of the ontology where the type of this action is defined.", "type": "string" }, - "resource_uid": { - "$ref": "#/definitions/resource_uid" + "resource_id": { + "$ref": "#/definitions/resource_id" }, "type": { "description": "The type of an action defines the class the action corresponds to.", @@ -253,12 +253,12 @@ "description": "Name of the context. It is a friendly name and not used for indexing.", "type": "string" }, - "ontology_uid": { + "ontology_id": { "description": "This is the UID of the ontology where the type of this context is defined.", "type": "string" }, - "resource_uid": { - "$ref": "#/definitions/resource_uid" + "resource_id": { + "$ref": "#/definitions/resource_id" }, "type": { "description": "The type of a context defines the class the context corresponds to.", @@ -469,12 +469,12 @@ "description": "Name of the event. It is a friendly name and not used for indexing.", "type": "string" }, - "ontology_uid": { + "ontology_id": { "description": "This is the UID of the ontology where the type of this event is defined.", "type": "string" }, - "resource_uid": { - "$ref": "#/definitions/resource_uid" + "resource_id": { + "$ref": "#/definitions/resource_id" }, "type": { "description": "The type of an event defines the class the event corresponds to.", @@ -907,12 +907,12 @@ "object_data_pointers": { "$ref": "#/definitions/element_data_pointers" }, - "ontology_uid": { + "ontology_id": { "description": "This is the UID of the ontology where the type of this object is defined.", "type": "string" }, - "resource_uid": { - "$ref": "#/definitions/resource_uid" + "resource_id": { + "$ref": "#/definitions/resource_id" }, "type": { "description": "The type of an object defines the class the object corresponds to.", @@ -1427,7 +1427,7 @@ "description": "Name of the relation. It is a friendly name and not used for indexing.", "type": "string" }, - "ontology_uid": { + "ontology_id": { "description": "This is the UID of the ontology where the type of this relation is defined.", "type": "string" }, @@ -1445,8 +1445,8 @@ }, "type": "array" }, - "resource_uid": { - "$ref": "#/definitions/resource_uid" + "resource_id": { + "$ref": "#/definitions/resource_id" }, "type": { "description": "The type of a relation defines the class the predicated of the relation corresponds to.", @@ -1461,8 +1461,8 @@ ], "type": "object" }, - "resource_uid": { - "description": "This is a JSON object that contains links to external resources. Resource_uid keys are strings containing numerical UIDs or 32 bytes UUIDs. Resource_uid values are strings describing the identifier of the element in the external resource.", + "resource_id": { + "description": "This is a JSON object that contains links to external resources. Resource_id keys are strings containing numerical UIDs or 32 bytes UUIDs. Resource_id values are strings describing the identifier of the element in the external resource.", "patternProperties": { "^(-?[0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$": { "type": "string" @@ -1652,12 +1652,12 @@ "additionalProperties": true, "description": "A tag is a special type of label that can be attached to any type of content, such as images, data containers, folders. In ASAM OpenLABEL the main purpose of a tag is to allow adding metadata to scenario descriptions.", "properties": { - "ontology_uid": { + "ontology_id": { "description": "This is the UID of the ontology where the type of this tag is defined.", "type": "string" }, - "resource_uid": { - "$ref": "#/definitions/resource_uid" + "resource_id": { + "$ref": "#/definitions/resource_id" }, "tag_data": { "$ref": "#/definitions/tag_data" @@ -1668,7 +1668,7 @@ } }, "required": [ - "ontology_uid", + "ontology_id", "type" ], "type": "object" diff --git a/tests/__test_assets__/openlabel_v1_short.json5 b/tests/__test_assets__/openlabel_v1_short.json5 index 7592e33..a8f4186 100644 --- a/tests/__test_assets__/openlabel_v1_short.json5 +++ b/tests/__test_assets__/openlabel_v1_short.json5 @@ -5,8 +5,7 @@ "schema_version": "1.0.0", "comment": "test_comment", "name": "test_project", - "subschema_version": "3.0.0", - "tagged_file": "test_folder" + "tagged_file": "test_folder", }, "streams": { "rgb_middle": { diff --git a/tests/__test_assets__/understand_ai_real_life.json b/tests/__test_assets__/understand_ai_real_life.json deleted file mode 100644 index d1dc17d..0000000 --- a/tests/__test_assets__/understand_ai_real_life.json +++ /dev/null @@ -1 +0,0 @@ -{"metadata": {"clip_id": "db_3_2021-09-22-14-28-01_2021-09-22-14-44-03", "external_clip_id": "2021-09-22-14-28-01_2021-09-22-14-44-03", "project_id": "trains_4", "export_time": "2023-04-20 01:38 UTC", "exporter_version": "1.0.0", "coordinate_system_3d": "FLU", "coordinate_system_reference": "SENSOR", "folder_name": "2021-09-22-14-28-01_2021-09-22-14-44-03"}, "coordinateSystems": [{"coordinate_system_id": "ir_left", "topic": "/A0001780/image", "frame_id": "A0001780", "position": [0.0907818, 0.398344, 3.5168], "rotation_quaternion": [0.996291, -2.43881e-05, 0.0254277, 0.0822105], "rotation_matrix": [0.9851897429851865, -0.1638122674201864, 0.05066272159526802, 0.1638097868835605, 0.986482876466111, 0.004229439715850778, -0.05067074143867226, 0.004132249000831645, 0.9987068641399489], "angle_axis_rotation": [-4.88367e-05, 0.0509184, 0.164625], "homogeneous_transform": [0.9851897429851865, -0.1638122674201864, 0.05066272159526802, 0.09078179057326469, 0.1638097868835605, 0.986482876466111, 0.004229439715850778, 0.3983440115030037, -0.05067074143867226, 0.004132249000831645, 0.9987068641399489, 3.516800000000002, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [3535.294117647, 0, 319.5, 0, 3535.294117647, 239.5, 0, 0, 1], "dist_coeffs": [0, 0, 0, 0, 0]}, {"coordinate_system_id": "ir_middle", "topic": "/A0001781/image", "frame_id": " A0001781", "position": [0.0933424, 0.225241, 3.5171], "rotation_quaternion": [0.999697, 0.00329686, 0.0243397, 0.00187897], "rotation_matrix": [0.998808097970439, -0.003596303890419965, 0.04867699689794021, 0.0039172823670546, 0.9999712003585413, -0.006500257868352383, -0.04865221811522157, 0.006683191739435895, 0.9987934203931477], "angle_axis_rotation": [0.00659439, 0.0486843, 0.00375831], "homogeneous_transform": [0.998808097970439, -0.003596303890419965, 0.04867699689794021, 0.09334241552161804, 0.0039172823670546, 0.9999712003585413, -0.006500257868352383, 0.2252408177142321, -0.04865221811522157, 0.006683191739435895, 0.9987934203931477, 3.517100000000001, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [3535.294117647, 0, 319.5, 0, 3535.294117647, 239.5, 0, 0, 1], "dist_coeffs": [0, 0, 0, 0, 0]}, {"coordinate_system_id": "ir_right", "topic": "/A0001782/image", "frame_id": "A0001782", "position": [0.0910011, 0.0528333, 3.5176], "rotation_quaternion": [0.99699, -0.000112887, 0.025241, -0.0733026], "rotation_matrix": [0.987979224228036, 0.1461583523732261, 0.05034668335960651, -0.1461697499026653, 0.9892534184834408, -0.003475375119154851, -0.05031358372408868, -0.003925563700978337, 0.9987257547707825], "angle_axis_rotation": [-0.000226001, 0.0505328, -0.146753], "homogeneous_transform": [0.987979224228036, 0.1461583523732261, 0.05034668335960651, 0.09100114488321256, -0.1461697499026653, 0.9892534184834408, -0.003475375119154851, 0.05283326726534611, -0.05031358372408868, -0.003925563700978337, 0.9987257547707825, 3.517600000000002, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [3535.294117647, 0, 319.5, 0, 3535.294117647, 239.5, 0, 0, 1], "dist_coeffs": [0, 0, 0, 0, 0]}, {"coordinate_system_id": "new_left_rect", "topic": "/S1213751/image", "frame_id": "S1213751", "position": [0.0609096, -0.136682, 3.51522], "rotation_quaternion": [0.980119, -0.0121052, 0.0606543, 0.188524], "rotation_matrix": [0.9215598505857764, -0.3710195436110039, 0.1143325852371231, 0.3680826063253607, 0.9286246600789553, 0.04659866536688102, -0.1234610736590737, -0.0008596231310027266, 0.9923490436026136], "angle_axis_rotation": [-0.0243722, 0.122119, 0.379566], "homogeneous_transform": [0.9215598505857764, -0.3710195436110039, 0.1143325852371231, 0.06090955407783759, 0.3680826063253607, 0.9286246600789553, 0.04659866536688102, -0.1366819504642395, -0.1234610736590737, -0.0008596231310027266, 0.9923490436026136, 3.515224928543994, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [7265.09513308939, 0, 2099.699693520321, 0, 7265.09513308939, 1217.709330768128, 0, 0, 1], "dist_coeffs": [-0.0878118, 0.0196239, 0, 0, 0.902557]}, {"coordinate_system_id": "new_middle_rect", "topic": "/S1213752/image", "frame_id": "S1213752", "position": [0.0801578, -0.332862, 3.50982], "rotation_quaternion": [0.998397, -0.00313306, 0.0562995, 0.00482918], "rotation_matrix": [0.9936140831557001, -0.009995656419306852, 0.1123883472936787, 0.009290097553920292, 0.9999337259704357, 0.006799834874476047, -0.1124488676781407, -0.005712312984130525, 0.9936410929698285], "angle_axis_rotation": [-0.00626947, 0.112659, 0.00966352], "homogeneous_transform": [0.9936140831557001, -0.009995656419306852, 0.1123883472936787, 0.08015775479489574, 0.009290097553920292, 0.9999337259704357, 0.006799834874476047, -0.3328620687352016, -0.1124488676781407, -0.005712312984130525, 0.9936410929698285, 3.509822884272755, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [7267.95450880415, 0, 2056.049238502414, 0, 7267.95450880415, 1232.862908875167, 0, 0, 1], "dist_coeffs": [-0.0764891, -0.188057, 0, 0, 1.78279]}, {"coordinate_system_id": "new_right_rect", "topic": "/S1213755/image", "frame_id": "S1213755", "position": [0.0510683, -0.525541, 3.51628], "rotation_quaternion": [0.980918, 0.0217394, 0.0639103, -0.182324], "rotation_matrix": [0.9253467445770058, 0.3604690081432737, 0.1174542313794686, -0.3549115208268265, 0.9325705804257798, -0.06595396052333095, -0.1333087194639806, 0.01934442277579149, 0.9908857545763512], "angle_axis_rotation": [0.0437575, 0.12864, -0.366986], "homogeneous_transform": [0.9253467445770058, 0.3604690081432737, 0.1174542313794686, 0.05106832805493725, -0.3549115208268265, 0.9325705804257798, -0.06595396052333095, -0.5255410107439861, -0.1333087194639806, 0.01934442277579149, 0.9908857545763512, 3.516282343577211, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [7265.854580654392, 0, 2093.506452810741, 0, 7265.854580654392, 1228.255759518024, 0, 0, 1], "dist_coeffs": [-0.0655375, -0.329857, 0, 0, 2.48002]}, {"coordinate_system_id": "rgb_left_rect", "topic": "/S1206062/image", "frame_id": "S1206062", "position": [0.0298925, 0.186612, 2.05637], "rotation_quaternion": [0.984704, 0.00228435, -0.0104673, 0.173904], "rotation_matrix": [0.9392957338063904, -0.3425356865117656, -0.01981988695799589, 0.3424400423651093, 0.9395044262429134, -0.008139438245733292, 0.02140891959199371, 0.0008582166902300636, 0.9997704344628401], "angle_axis_rotation": [0.00459215, -0.021042, 0.349592], "homogeneous_transform": [0.9392957338063904, -0.3425356865117656, -0.01981988695799589, 0.02989252679403986, 0.3424400423651093, 0.9395044262429134, -0.008139438245733292, 0.1866115905585829, 0.02140891959199371, 0.0008582166902300636, 0.9997704344628401, 2.05636627359921, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [4622.041473915607, 0, 1233.380196060109, 0, 4622.041473915607, 843.3909933480334, 0, 0, 1], "dist_coeffs": [-0.0868757, 0.53867, 0, 0, 1.69009]}, {"coordinate_system_id": "rgb_middle_rect", "topic": "/S1206063/image", "frame_id": "S1206063", "position": [0.0669458, -0.000911152, 2.05432], "rotation_quaternion": [0.99999, 0.00193374, -0.00293566, 0.00271082], "rotation_matrix": [0.9999680667369587, -0.005432933661727504, -0.005860779656024867, 0.005410226430795589, 0.9999778242364062, -0.003883360064516633, 0.005881747726375861, 0.003851527911158113, 0.9999752850828029], "angle_axis_rotation": [0.00386749, -0.00587134, 0.00542165], "homogeneous_transform": [0.9999680667369587, -0.005432933661727504, -0.005860779656024867, 0.06694577073389593, 0.005410226430795589, 0.9999778242364062, -0.003883360064516633, -0.0009111521949818156, 0.005881747726375861, 0.003851527911158113, 0.9999752850828029, 2.054322280217477, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [4609.471892628096, 0, 1257.158605934, 0, 4609.471892628096, 820.0498076210201, 0, 0, 1], "dist_coeffs": [-0.0914603, 0.605326, 0, 0, 0.417134]}, {"coordinate_system_id": "rgb_right_rect", "topic": "/S1206064/image", "frame_id": "S1206064", "position": [0.051346, -0.196979, 2.05803], "rotation_quaternion": [0.982823, 0.00740182, -0.0113748, -0.184053], "rotation_matrix": [0.9319898651765356, 0.3616154118829123, -0.02508356228637691, -0.3619521900584727, 0.9321390652699187, -0.01036219617635507, 0.01963423846493898, 0.01873651212135621, 0.9996316520566103], "angle_axis_rotation": [0.014889, -0.0228808, -0.370229], "homogeneous_transform": [0.9319898651765356, 0.3616154118829123, -0.02508356228637691, 0.05134595505315692, -0.3619521900584727, 0.9321390652699187, -0.01036219617635507, -0.1969787994760381, 0.01963423846493898, 0.01873651212135621, 0.9996316520566103, 2.05803483265557, 0, 0, 0, 1], "measured_position": [0, 0, 0], "camera_matrix": [4613.465257442901, 0, 1230.818284106724, 0, 4613.465257442901, 783.2495217495479, 0, 0, 1], "dist_coeffs": [-0.0888195, 0.52648, 0, 0, 1.27408]}, {"coordinate_system_id": "lidar_merged", "topic": "/lidar_merged", "frame_id": "lidar_merged", "position": [0, 0, 0], "rotation_quaternion": [1, 0, 0, 0], "rotation_matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "angle_axis_rotation": [0, 0, 0], "homogeneous_transform": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]}, {"coordinate_system_id": "imu", "topic": "/novatel/oem7/inspva", "frame_id": "gps", "position": [-0.0128436, -0.34586, 1.40235], "rotation_quaternion": [0.708951, 0.00553888, -0.00646254, -0.705207], "rotation_matrix": [0.005283316568778951, 0.9998419494966226, -0.01697535250965811, -0.9999851303902971, 0.005305487067641945, 0.001261271293448576, 0.001351134462094406, 0.01696843639726932, 0.999855112804799], "angle_axis_rotation": [0.0122955, -0.0143459, -1.56546], "homogeneous_transform": [0.005283316568778951, 0.9998419494966226, -0.01697535250965811, -0.01284360283877562, -0.9999851303902971, 0.005305487067641945, 0.001261271293448576, -0.3458604234242062, 0.001351134462094406, 0.01696843639726932, 0.999855112804799, 1.402350000000001, 0, 0, 0, 1]}, {"coordinate_system_id": "radar", "rotation_around_z_in_degrees": 1.22869, "topic": "/talker1/Nvt/Polar", "frame_id": "navtech", "position": [-0.0029401, 0.329699, 1.5638], "rotation_quaternion": [0.999943, 0, 0, 0.0107221], "rotation_matrix": [0.9997700727912402, -0.02144298372425017, 0, 0.02144298372425017, 0.9997700727912402, 0, 0, 0, 1], "angle_axis_rotation": [0, 0, 0.0214446], "homogeneous_transform": [0.9997700727912402, -0.02144298372425017, 0, -0.002940099322541904, 0.02144298372425017, 0.9997700727912402, 0, 0.3296990382394244, 0, 0, 1, 1.5638, 0, 0, 0, 1]}], "frames": [{"frameId": "000", "timestamp": "1632321880.132833000", "annotations": {"2D_BOUNDING_BOX": [{"id": "2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965", "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", "className": "2D_person", "geometry": {"xMin": 335.0613528455474, "yMin": 73.37014729199004, "xMax": 340.6805183501037, "yMax": 91.12742254778364}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "age": "adult", "function": "worker", "occlusion": "0-25%", "pose": "upright"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "720270fc-a11e-4b06-983e-203d1595c69b", "objectId": "87563e91-2b69-475b-9a8b-07f794d1bdb2", "className": "2D_person", "geometry": {"xMin": 304.2236167003333, "yMin": 72.23946974536051, "xMax": 310.3921519920513, "yMax": 92.01238967968362}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "function": "worker", "age": "adult", "occlusion": "0-25%", "pose": "upright"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "bb73422d-e2cd-4fc7-86e5-5f0df285a7b7", "objectId": "817e2a1c-63fe-4a6e-a695-96d7f4d548f5", "className": "2D_person", "geometry": {"xMin": 188.40749405733118, "yMin": 64.72499707814356, "xMax": 194.09358061839814, "yMax": 73.98141706127583}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "pose": "upright", "function": "passenger", "age": "adult", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "0a3375c8-23f4-412d-85a1-8de5cf4be4d9", "objectId": "d208f5e6-bc1a-44ef-9c54-88450cae72ca", "className": "2D_person", "geometry": {"xMin": 177.17953517456948, "yMin": 63.79344340785774, "xMax": 182.86562173563644, "yMax": 73.04986339099001}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "pose": "upright", "function": "passenger", "age": "adult", "occlusion": "75-100%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "f1e5e16e-5c86-47a9-974f-cf17eeb33717", "objectId": "1fb2a092-c6ce-4e65-aaae-7dd21ffc1ece", "className": "2D_crowd", "geometry": {"xMin": 225.3342096763332, "yMin": 58.01514036957722, "xMax": 237.54465336640024, "yMax": 72.38036824024432}, "attributes": {"size": "<25"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "da4fdbe6-05b3-4497-8fd6-950919e71d44", "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", "className": "2D_person", "geometry": {"xMin": 2118.896487190378, "yMin": 432.5018147440208, "xMax": 2146.148097005178, "yMax": 465.98069983342845}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "age": "adult", "function": "worker", "occlusion": "0-25%", "pose": "upright"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "4ab857e4-4a9a-40c0-bf77-d498143c7ea6", "objectId": "87563e91-2b69-475b-9a8b-07f794d1bdb2", "className": "2D_person", "geometry": {"xMin": 2063.7330499901377, "yMin": 432.9369653978952, "xMax": 2073.9456499222456, "yMax": 455.5466196120661}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "function": "worker", "age": "adult", "occlusion": "0-25%", "pose": "upright"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "bb5b00c0-a51f-454b-97c1-b73ac096ca1a", "objectId": "1fb2a092-c6ce-4e65-aaae-7dd21ffc1ece", "className": "2D_crowd", "geometry": {"xMin": 1904.5268781542197, "yMin": 403.10555844651935, "xMax": 1928.2916999767085, "yMax": 427.5324127178042}, "attributes": {"size": "<25"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "f5ef4106-a620-413c-b8a5-f2643053f275", "objectId": "817e2a1c-63fe-4a6e-a695-96d7f4d548f5", "className": "2D_person", "geometry": {"xMin": 1826.4236038945085, "yMin": 409.76178376153325, "xMax": 1837.4765795972799, "yMax": 433.2668460155281}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "pose": "upright", "function": "passenger", "age": "adult", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "e77a92ba-24f0-433f-ac2c-db1ae1e1420e", "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", "className": "2D_person", "geometry": {"xMin": 1282.8870302764296, "yMin": 841.7938065809931, "xMax": 1290.1108660829127, "yMax": 862.5315711622637}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "age": "adult", "function": "worker", "occlusion": "0-25%", "pose": "upright"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "d4e504f5-e390-4567-ba60-6f921dc29ec8", "objectId": "87563e91-2b69-475b-9a8b-07f794d1bdb2", "className": "2D_person", "geometry": {"xMin": 1244.6318807670598, "yMin": 841.6253587738138, "xMax": 1251.4123764868605, "yMax": 862.4102202314651}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "function": "worker", "age": "adult", "occlusion": "0-25%", "pose": "upright"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}], "2D_POLYLINE": [{"id": "7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2", "objectId": "4d8eca35-6c1d-4159-8062-21c2f2c051df", "className": "ir_track", "geometry": {"points": [[493.20616179847406, 479.9728191806302], [542.6546426503735, 431.74800354374366], [589.9350150621058, 385.51547643522207], [639.9702598289472, 335.8846553104306]]}, "attributes": {"trackId": "left_first_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_left", "uri": "A0001780_image/000_1632321843.100464380.png", "timestamp": "1632321843.100464380"}}, {"id": "3fb1fec6-9678-4f17-a9f9-5c3b1025bd4f", "objectId": "9aaac1af-fd50-4e87-9c69-d4c68440e741", "className": "ir_track", "geometry": {"points": [[313.0613164798889, 479.950990209001], [369.2321788528948, 440.19873150950764], [408.0805934961111, 413.4535348241989], [554.2742281489762, 310.98987264066847], [639.9596760216506, 250.63394942730864]]}, "attributes": {"occlusion": "0-25%", "railSide": "leftRail", "trackId": "left_first_track"}, "sensor": {"type": "ir_left", "uri": "A0001780_image/000_1632321843.100464380.png", "timestamp": "1632321843.100464380"}}, {"id": "e7643eea-91eb-410d-807e-65185267c96e", "objectId": "9394e5a5-4e10-40d3-97f1-38d2863fa0e8", "className": "ir_track", "geometry": {"points": [[0.03437888727654311, 396.32454437331694], [215.22773808785624, 318.6084462214661], [314.05208340365675, 283.71633252244044], [400.56402223701974, 252.50871268332327], [464.13052182889817, 229.98832632936805], [525.8464409491382, 208.55610276470978], [562.5647293996449, 194.64288217157042], [592.5721219482407, 183.45991089676374], [618.2702255735703, 173.13056538390248], [639.9499715405988, 163.9732386190436]]}, "attributes": {"trackId": "left_second_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_left", "uri": "A0001780_image/000_1632321843.100464380.png", "timestamp": "1632321843.100464380"}}, {"id": "5c39d55d-54c8-4ed2-8c31-b6843156ae16", "objectId": "7e78d664-6dd2-4b17-9117-3b4f79f2bf6a", "className": "ir_track", "geometry": {"points": [[-0.004904975156543968, 348.01217475488], [470.7348877753093, 203.85478011624818], [580.2591036269735, 167.4198179772809], [639.9693979825845, 146.84127443269193]]}, "attributes": {"trackId": "left_second_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_left", "uri": "A0001780_image/000_1632321843.100464380.png", "timestamp": "1632321843.100464380"}}, {"id": "1faeae82-911e-469e-aa36-78655d8f575d", "objectId": "d8a95754-f499-42f3-a4f7-4fdc375d9741", "className": "ir_track", "geometry": {"points": [[455.3800487473675, 479.98732853302573], [350.7176588703522, 113.00273667746536], [347.9771959854138, 101.651250445595], [344.5496129207221, 96.16711754208822], [336.3234135654619, 90.13457134823078], [325.3551477584484, 82.59388860590899], [319.70358796071935, 77.88853542725904], [317.0068934833211, 73.42564652000226]]}, "attributes": {"trackId": "ego_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "f87cff6f-086e-495b-8b09-c0ef1af44905", "objectId": "edd2e6a9-0de8-4081-baa3-4a85789b015e", "className": "ir_track", "geometry": {"points": [[0.042921983620601914, 395.9037371950096], [170.68694231780856, 228.13640564378], [282.15031405027196, 114.42578851348473], [294.40051606069954, 100.34334741607137], [295.4344412830434, 97.2066114961728], [293.97373177445263, 89.22889033386944], [292.17593545618706, 79.79045966297534], [291.7264863766207, 75.97014248666106], [292.17593545618706, 73.16108573937115]]}, "attributes": {"trackId": "left_first_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "fd552130-d990-4935-a846-ec1963f75dc9", "objectId": "bc96ede0-181a-4b2f-b5c0-cc38209b7afe", "className": "ir_track", "geometry": {"points": [[0.0861200955535391, 298.3877763294074], [181.615945555359, 168.20615848280275], [245.88469188942295, 122.77225914737839], [262.77022801342713, 110.09062391268064], [277.0269882006692, 99.26995121369254], [279.20347623027305, 96.36428354054253], [279.68172058794295, 91.5135193413194], [280.2282855681371, 81.88031156539739], [281.1164536609526, 76.96122674364997], [282.21004569888004, 72.71356653819255]]}, "attributes": {"trackId": "left_first_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "179a69ee-1365-4b01-81f7-d3fe1a2f69b4", "objectId": "511c10af-7c23-45ad-9584-b7fb93762b68", "className": "ir_track", "geometry": {"points": [[0.10208334064097657, 198.03651774822134], [89.66049694043467, 161.5013529320002], [158.64383310143512, 134.2116230737762], [221.930332918142, 105.99877021821356], [238.5864827314941, 97.58587740967494], [248.0997627770862, 90.87966301609207], [260.26331201668734, 79.95729227032783], [263.24214040189577, 75.11669614436413], [262.99390470312835, 70.40021786778412], [258.029190727781, 66.05609313935516], [246.85858428324934, 60.470789917089355]]}, "attributes": {"trackId": "left_second_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "5b7bd88c-6c67-4bed-a1f2-d68f41900482", "objectId": "8f787a6e-7156-4d86-86a4-639c054e9fe4", "className": "ir_track", "geometry": {"points": [[0.04951429204127547, 178.50135778524762], [174.4205176500624, 116.9579683475495], [229.32378878234135, 94.64266281011702], [235.4848916312007, 90.89870824457981], [241.39203407487483, 86.7275001138147], [254.03253590263736, 76.4619575236806], [256.3261294725185, 73.04492452249436], [254.5884795811469, 68.82491764344908], [244.9072873292195, 62.49490732488117]]}, "attributes": {"trackId": "left_second_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "c258279c-c91d-4cb6-a4aa-495617a75780", "objectId": "6f09ed53-50fb-40b0-ab6a-a786e14aa3ca", "className": "ir_track", "geometry": {"points": [[275.6169521207512, 479.76386705594814], [301.4933431973199, 298.05767596796363], [320.97521361540646, 163.6165139117071], [328.858614856986, 111.1039643034198], [329.96822217025834, 99.95949983903851], [329.46056383852846, 97.88498656129265], [328.3061313614731, 96.37867054792011], [313.6263103156072, 83.55745217109013], [309.00460911729783, 79.01870745395739], [307.86157752643675, 73.53932787206075]]}, "attributes": {"trackId": "ego_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "be9a72de-69f2-432a-8b14-800132c93c00", "objectId": "4d8eca35-6c1d-4159-8062-21c2f2c051df", "className": "2D_track", "geometry": {"points": [[2252.521919069662, 2494.8555467437595], [4105.793257512472, 1040.6051634805779]]}, "attributes": {"trackId": "left_first_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "new_left_rect", "uri": "S1213751_image/000_1632321843.100000003.png", "timestamp": "1632321843.100000003"}}, {"id": "bde752be-6feb-4fa0-90a5-3668233f10f8", "objectId": "9aaac1af-fd50-4e87-9c69-d4c68440e741", "className": "2D_track", "geometry": {"points": [[1232.7582968238873, 2495.7360659583305], [3722.798427272396, 1068.660656794148], [4106.676388260239, 847.4296538290098]]}, "attributes": {"occlusion": "0-25%", "railSide": "leftRail", "trackId": "left_first_track"}, "sensor": {"type": "new_left_rect", "uri": "S1213751_image/000_1632321843.100000003.png", "timestamp": "1632321843.100000003"}}, {"id": "b5035d8d-317a-40df-8e7d-5329aff67029", "objectId": "9394e5a5-4e10-40d3-97f1-38d2863fa0e8", "className": "2D_track", "geometry": {"points": [[5.141954821974915, 1907.7194687329416], [2882.787217971907, 1010.9085018107056], [4106.6149685702085, 630.7755159484894]]}, "attributes": {"trackId": "left_second_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "new_left_rect", "uri": "S1213751_image/000_1632321843.100000003.png", "timestamp": "1632321843.100000003"}}, {"id": "dfc01a89-2399-4d1a-b9ae-afa70064fec9", "objectId": "7e78d664-6dd2-4b17-9117-3b4f79f2bf6a", "className": "2D_track", "geometry": {"points": [[4.995198903747977, 1686.48035498139], [1313.358240005028, 1338.1991865943585], [2141.896352037657, 1118.2479968669118], [2848.151285292086, 926.3600007293461], [3309.4870983201954, 803.471421713125], [3763.544725492926, 681.9224418759578], [4107.385075980987, 586.5791386584722]]}, "attributes": {"trackId": "left_second_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "new_left_rect", "uri": "S1213751_image/000_1632321843.100000003.png", "timestamp": "1632321843.100000003"}}, {"id": "c7cf7644-9efa-436d-b51f-22a733bedee8", "objectId": "d8a95754-f499-42f3-a4f7-4fdc375d9741", "className": "2D_track", "geometry": {"points": [[2379.5501498790773, 2495.843175640668], [2146.1995006855764, 494.71563966703246], [2144.187805539245, 485.7495968229351], [2137.5994424088235, 477.816795841213], [2122.0509054210306, 465.05390113950074], [2106.539023171181, 451.9333233005486], [2096.657263973746, 441.53393022533686], [2093.066548582037, 436.2418751923561], [2092.3285817292713, 432.7707124257557], [2092.8428183454807, 429.16303997772394]]}, "attributes": {"trackId": "ego_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "93429ed1-e0d0-4bff-a591-45b40ffe0979", "objectId": "edd2e6a9-0de8-4081-baa3-4a85789b015e", "className": "2D_track", "geometry": {"points": [[4.250803034374943, 2191.4912534176565], [880.1070130443643, 1467.020135582246], [1557.3651181322214, 903.8208231780118], [1791.2392583338794, 703.5098464202181], [1974.8790868836134, 546.1371936384963], [2017.5062809544963, 508.1924791398625], [2043.4085436040327, 483.41879742844907], [2044.3676311728739, 479.7340444663073], [2044.1040966476571, 469.85149977067584], [2042.7864240215729, 452.06291931853923], [2041.864053183314, 436.5143823307458], [2041.864053183314, 432.5613644524932], [2042.522889496356, 430.8483900385837], [2044.2358639102654, 429.6624846751079]]}, "attributes": {"trackId": "left_first_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "f8fb8eec-ee0b-4824-a85c-9ad09aa9acb7", "objectId": "bc96ede0-181a-4b2f-b5c0-cc38209b7afe", "className": "2D_track", "geometry": {"points": [[5.077810430167757, 1715.6012098494468], [746.7278472456529, 1268.7187827868531], [1182.4449633668828, 1004.181244382544], [1543.668383876772, 779.9769318855965], [1681.949798042588, 692.2100453384039], [1836.5346787869812, 594.2048612512249], [1949.4897810082625, 522.2759046480503], [1993.1799620561167, 493.68192436876365], [2006.3225368428696, 485.1570109935726], [2011.4084943686266, 480.3718719514265], [2014.4391414086203, 471.8069998818791], [2017.469788448614, 459.4208771966875], [2021.4228063268667, 442.8182021080264], [2022.6087116903425, 433.0674246750032], [2023.399315265993, 431.0909157358769], [2025.244056942511, 429.7732431097927]]}, "attributes": {"trackId": "left_first_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "a739ac4b-54d3-48e4-8478-7c5e92f7a324", "objectId": "c0ca7a4b-d4cf-40b1-aef8-77958a7a4af1", "className": "2D_track", "geometry": {"points": [[5.345579852404898, 1139.6289554668192], [626.4784259642481, 937.213162344719], [1186.2924335566424, 754.3446449144233], [1360.2973771359923, 696.3790486408805], [1492.28813472448, 649.1197869502381], [1592.5630215694891, 611.5661171068379], [1687.5672179101614, 576.3842579903892], [1765.836971899564, 547.2636929539278], [1832.6429740420342, 519.5925678061592], [1895.8912600940769, 494.556787910559], [1930.282515634875, 480.0623890236326], [1937.6614823409466, 475.7140693575547], [1948.2028633496204, 466.0950591871399], [1965.3326074887152, 450.54652219934604], [1980.3596038435626, 435.0095149716398]]}, "attributes": {"trackId": "left_second_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "b6752d58-4bd5-4994-b447-6be889db56cc", "objectId": "2f789702-5056-49ea-8fd7-4b38e5a39246", "className": "2D_track", "geometry": {"points": [[5.07677327878637, 1042.1135161596303], [170.4232991339228, 995.8398154443826], [748.3073832673617, 832.7769644121568], [1213.6444983060373, 701.7432622348484], [1429.3175611910574, 634.4286844057459], [1579.9741292617484, 587.0372539140964], [1671.940919277424, 557.3153599926733], [1773.3937942772338, 522.4258609832299], [1862.9488677715194, 490.7519840023067], [1901.3755510123349, 476.60324707681815], [1917.7026285795296, 467.2683838055674], [1940.8455609349885, 451.1119970668506], [1956.8563946400232, 438.73998920386936], [1962.4032946518846, 432.8590103378675]]}, "attributes": {"trackId": "left_second_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "c272b523-6c53-4b31-a542-c0b76a210b07", "objectId": "2b277f59-5aba-4e3a-a6e5-1c7ec73628af", "className": "2D_track", "geometry": {"points": [[1481.4807803515253, 2495.7243916334887], [2034.0941709659146, 739.1257762075705], [2089.995734596434, 554.1828905042668], [2109.716220214274, 489.17030055534343], [2110.5830547469263, 484.8361278920819], [2107.7658425158065, 478.33486889718955], [2091.5126950285758, 460.78146961098025], [2078.523305751377, 445.9933302489528], [2075.146902621431, 440.20521059761643], [2074.503778215727, 434.5778720477061], [2075.307683722857, 431.8445933234639], [2077.076275838543, 429.27209570064775]]}, "attributes": {"trackId": "ego_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "44252034-cce6-453c-8910-8a9b54c80e2d", "objectId": "4d8eca35-6c1d-4159-8062-21c2f2c051df", "className": "rgb_track", "geometry": {"points": [[1697.6940378391973, 1598.8718529609541], [2462.6073330906743, 1189.691203687655]]}, "attributes": {"trackId": "left_first_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_left_rect", "uri": "S1206062_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "5b3dce74-6f32-4bb5-969b-8f41c7cb5687", "objectId": "9aaac1af-fd50-4e87-9c69-d4c68440e741", "className": "rgb_track", "geometry": {"points": [[1179.7577877572107, 1597.7023638171684], [1814.6619167783147, 1361.259718597823], [2260.1802401895616, 1192.8788439174455], [2462.7756263729925, 1114.6508187475927]]}, "attributes": {"trackId": "left_first_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_left_rect", "uri": "S1206062_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "a89176ac-b0a1-459a-8a3c-b6fb3ef2a8a8", "objectId": "9394e5a5-4e10-40d3-97f1-38d2863fa0e8", "className": "rgb_track", "geometry": {"points": [[0.9615269441112031, 1490.782328847835], [820.1650531369063, 1340.2897542982923], [1653.8745396593645, 1187.2560075008341], [2064.7904123234894, 1113.062481915935], [2328.9924108088576, 1064.429998018095], [2462.7681142942874, 1038.0277324303006]]}, "attributes": {"trackId": "left_second_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_left_rect", "uri": "S1206062_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "5ad8a9e6-cb18-4158-8414-068e2850705b", "objectId": "7e78d664-6dd2-4b17-9117-3b4f79f2bf6a", "className": "rgb_track", "geometry": {"points": [[0.973375676160646, 1411.66629468312], [2154.4421919389893, 1075.249559114343], [2255.5205569104205, 1059.6054197985973], [2366.3809025509204, 1040.4539620173637], [2431.4565333652354, 1029.0985064023366], [2462.685987745156, 1023.6095675159935]]}, "attributes": {"trackId": "left_second_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_left_rect", "uri": "S1206062_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "9ee274f7-c063-4a66-b184-02eb76d085e6", "objectId": "2f789702-5056-49ea-8fd7-4b38e5a39246", "className": "rgb_track", "geometry": {"points": [[0.18228617103899536, 1071.6870851605834], [196.34242681053047, 1042.0503963145543], [366.58313055707964, 1014.339496473875], [618.364621738675, 971.456430220993], [791.6865348442586, 941.6599109837097], [935.7305485431075, 912.0274595371485], [1031.3587574100368, 891.5582169979011], [1106.0624139634529, 874.5702134668036], [1138.0362066428231, 865.7995270811073], [1150.0965153523837, 861.8335046257143], [1156.78005445836, 858.6702170137303], [1173.7304788658505, 850.7062660675313], [1177.0613374409113, 847.3475553848799], [1178.5977011897958, 842.5921437811903], [1177.7197790475761, 840.1778578900864], [1174.3751985056422, 836.1322469550021], [1166.0280089312532, 829.049568085332]]}, "attributes": {"trackId": "left_second_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "eeea7212-cff8-4a17-b523-b3c2f16a69d0", "objectId": "edd2e6a9-0de8-4081-baa3-4a85789b015e", "className": "rgb_track", "geometry": {"points": [[0.9919881666479418, 1549.0804042409159], [635.1167749034076, 1204.612867249191], [894.2759820805727, 1064.1332960156167], [1151.2358883471509, 918.0928678125239], [1209.1736849650497, 884.460890262369], [1232.2943532571012, 870.2991582966198], [1233.542797048765, 868.8393021180595], [1233.8318623571056, 866.2529401466634], [1233.614593380072, 863.4464593311877], [1231.5574990532073, 856.6623885714913], [1230.026683675694, 845.3685263808782]]}, "attributes": {"trackId": "left_first_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "3eedec8b-6b0f-4a39-b4bc-41bfc941f387", "objectId": "bc96ede0-181a-4b2f-b5c0-cc38209b7afe", "className": "rgb_track", "geometry": {"points": [[-0.023919849634604078, 1351.059408407321], [353.9662095569844, 1215.766658807716], [371.1304190193142, 1208.9230514674648], [424.45353460913617, 1186.096308441622], [586.581805605285, 1122.998025052844], [745.7386816912161, 1060.3541444562288], [836.533912823775, 1024.0733342554736], [901.864552261756, 998.1084389880424], [1007.2122845901209, 953.9732978278183], [1109.0925132218777, 911.4893831405211], [1144.8830735876272, 896.6229767802525], [1173.5766184298927, 884.3325198391425], [1199.0799298938393, 873.1505816500435], [1206.5242556797848, 869.6859633594977], [1211.9589545757835, 866.7127238816331], [1212.8946231809143, 865.6622162947929], [1215.0785654769588, 858.2680903454101], [1215.738103694248, 852.8551631650888], [1216.1863929415165, 845.1993836478916]]}, "attributes": {"trackId": "left_first_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "93192984-67c3-444e-98db-9183d10022d9", "objectId": "c0ca7a4b-d4cf-40b1-aef8-77958a7a4af1", "className": "rgb_track", "geometry": {"points": [[0.09355045728567468, 1112.1310682517997], [194.19386776028125, 1073.866343194241], [380.9003638327005, 1039.2098778035734], [634.1775413304065, 989.1480116096307], [814.7116436690734, 952.3947646335607], [889.2271092927772, 936.0246211466389], [1026.4910491788678, 903.0066924560692], [1123.958441001044, 879.8319355933664], [1161.7488442931465, 868.3358535958287], [1166.2283490824573, 866.9771096021766], [1172.4147190807312, 863.465844042239], [1185.4985377487408, 853.4772166997619], [1191.1369323805438, 845.3668078772386]]}, "attributes": {"trackId": "left_second_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "229b3aac-c30f-424f-9e81-bc099486a458", "objectId": "d8a95754-f499-42f3-a4f7-4fdc375d9741", "className": "rgb_track", "geometry": {"points": [[1569.4467563030828, 1599.0113549693926], [1400.1520119851261, 1149.098552772457], [1347.0118107432909, 1006.0178244791554], [1324.6663399063134, 946.1537614390404], [1315.5730149803705, 922.2135067312662], [1307.7709343941797, 900.2678026828253], [1303.3165651042013, 888.447253426436], [1298.0031450316474, 872.6178453523355], [1297.3261552762115, 870.5523148223498], [1296.285952914028, 869.0782256913335], [1294.8865433308422, 868.1206994097241], [1283.6647996949398, 862.7922763001277], [1273.638113981567, 856.8142979551934], [1268.1670071961612, 853.7800151782487], [1266.8877136194494, 852.8467900953069], [1265.585833449315, 851.6699266608371], [1262.0460316475687, 846.4888788027661]]}, "attributes": {"trackId": "ego_track", "railSide": "rightRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "ab342776-e52b-4622-b1c7-09aaf429dade", "objectId": "20d912d4-275b-4af1-99c1-fbcbc4f8de72", "className": "rgb_track", "geometry": {"points": [[1013.8586324572403, 1597.8954721388798], [1074.47887328636, 1433.1813889440893], [1149.4604076399721, 1225.397633872001], [1210.508256445707, 1055.6377298078028], [1248.6586752454152, 947.922607453482], [1265.6170957954719, 899.8415241708789], [1272.8875729395256, 876.4035884979564], [1274.1648384628043, 871.773219127553], [1274.2630896569026, 869.1103862886732], [1273.064696524136, 867.4608645990032], [1271.315553833952, 865.8680968834275], [1263.6519606942802, 861.4643816958942]]}, "attributes": {"trackId": "ego_track", "railSide": "leftRail", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}], "2D_POLYGON": [{"id": "0f90cffa-2b6b-4e09-8fc2-527769a94e0a", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "2D_signal_pole", "geometry": {"points": [[127.71153737657284, -0.3861000079676791], [127.4762636010818, 328.04436391207815], [115.77703250958459, 334.4789410124016], [115.01063176442402, 411.0810690770479], [172.81920159167288, 415.5183358018615], [174.2731879670706, 333.8939794578268], [146.77999490205218, 330.3842101303776], [147.9547466526339, -0.38610000796767874], [127.71153737657284, -0.3861000079676791]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_left", "uri": "A0001780_image/000_1632321843.100464380.png", "timestamp": "1632321843.100464380"}}, {"id": "1949fa2e-baf2-4d49-98d7-6dd6fa49458d", "objectId": "c8ef60c9-73b9-49cd-a2e8-14588b7c0be8", "className": "2D_catenary_pole", "geometry": {"points": [[512.0809299028974, 0], [505.77495910572713, 205.17385354249294], [497.20072729722733, 212.82512282789605], [507.127283700637, 221.48304004969694], [547.315035761125, 216.59671445724544], [538.6611785160225, 202.61048178897374], [537.1322264443298, 0.8650673820311914], [512.0809299028974, 0]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "1e84c08e-a0a1-4ba1-b0a0-297d7e10a58a", "objectId": "5029d606-305c-4276-9cb9-5ae2ef61d285", "className": "2D_catenary_pole", "geometry": {"points": [[449.8045038171237, 0.4777862850816007], [448.3458460072902, 164.7080127849633], [469.789595777664, 162.6237479377495], [465.2513858132714, 0.4777862850816007], [449.8045038171237, 0.4777862850816007]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "61745003-0a68-4ad3-9495-a9917bac63cc", "objectId": "e32d3223-1292-4c78-b0cd-46ce6ad9a0bc", "className": "2D_catenary_pole", "geometry": {"points": [[422.8037496259689, -0.2388931425408004], [421.1691548634797, 144.9593612973092], [436.3450881109681, 145.06404701698122], [436.60164577691046, 0.2388931425408003], [422.8037496259689, -0.2388931425408004]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "3d1e6e20-f51a-44c1-9721-6bd37196a669", "objectId": "c305af21-7b22-402f-8cda-3d7b76e9d073", "className": "2D_catenary_pole", "geometry": {"points": [[409.82057545965785, 0], [406.39919882866906, 132.9858412563816], [423.226131513968, 131.8623469915388], [421.24031066294435, 0], [409.82057545965785, 0]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "298895cf-5fdb-4c69-96f6-ec6828c6f01d", "objectId": "0650ff28-41a6-4a5f-ac17-89a769d5ffad", "className": "2D_catenary_pole", "geometry": {"points": [[395.8629008832957, -0.3219918630540872], [393.40305776132107, 116.15508672316572], [406.01770020382617, 116.08050674337235], [406.4205548600286, 115.9063554935946], [406.59674871466507, 0.0], [395.8629008832957, -0.3219918630540872]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "41e8e05b-f72f-47b2-ac5a-eb38be544c31", "objectId": "e03bf406-e5fb-4b6b-8bfb-4388de4b1cf1", "className": "2D_catenary_pole", "geometry": {"points": [[384.3938844492276, 30.911218853192374], [385.3594701100145, 110.3174038948946], [395.70644264619784, 110.8829740098017], [395.7403643468395, 110.72344820809218], [393.50694464223807, 31.877194442354636], [384.3938844492276, 30.911218853192374]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "50-75%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "add8316f-90c0-445b-8467-bc56ae3d55f6", "objectId": "27c9e593-ff11-4b1c-9253-aff4706ddb5b", "className": "2D_catenary_pole", "geometry": {"points": [[383.1504165882574, 30.844804203948534], [376.2412852574384, 30.59240121870129], [376.7710454583902, 105.39564002544259], [376.8342796289278, 105.50434150729063], [386.94934177489785, 105.43778451505278], [383.1504165882574, 30.844804203948534]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "34193907-b80b-424b-9ec6-fd0d1e6965a8", "objectId": "0da518da-8c14-4364-b494-646809641015", "className": "2D_catenary_pole", "geometry": {"points": [[374.1946290053524, 30.49938335904774], [368.13540779957196, 30.04811647302623], [367.8264341929743, 100.33618792895052], [367.9081997625635, 100.41536301885891], [376.65205686503003, 100.35721176521533], [374.1946290053524, 30.49938335904774]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "27a8c247-7bbb-4b79-a868-783d27036699", "objectId": "2165789a-d0d5-42df-93b7-192189b558a2", "className": "2D_catenary_pole", "geometry": {"points": [[361.2793866997643, 29.999957149058368], [354.70154716491754, 30.053567176242392], [355.7820615181083, 95.99573637615208], [363.2261794624086, 95.94802522778559], [363.3866050526753, 95.88680928293033], [361.2793866997643, 29.999957149058368]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "422502de-4154-4cf2-b604-afce0aba6187", "objectId": "e9b747e7-e01f-4209-b082-6986ce17ce2d", "className": "2D_catenary_pole", "geometry": {"points": [[351.64125665066695, 30.188239436424734], [346.04153142738704, 29.356694690560282], [346.14161825086967, 92.26536713305427], [346.22087616968696, 92.3115417619917], [352.6884472192111, 92.26807190232373], [351.64125665066695, 30.188239436424734]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "6026324d-560a-4dd7-a233-57d312ef552e", "objectId": "bb1f0700-9c85-4560-a998-fb3f04eeb296", "className": "2D_catenary_pole", "geometry": {"points": [[343.0333131437304, 29.114267607754385], [338.96089960511915, 29.444904810639283], [339.37616291760537, 88.3712833715512], [339.3963402913408, 88.40596692905395], [345.2122881266227, 88.36713710047184], [343.0333131437304, 29.114267607754385]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "2388337a-f7d3-419f-873c-a42283162c12", "objectId": "3c0bd8ef-d8ce-4e9d-90cf-59fdb64fce7c", "className": "2D_train_front", "geometry": {"points": [[175.0800966023239, 53.26718137932871], [158.01247240932605, 53.41408743373945], [150.77128042272258, 58.65338309013694], [150.66540159428502, 78.74906119770993], [162.96210460359504, 83.37942828553], [174.2934160452979, 84.20193713075852], [175.0800966023239, 53.26718137932871]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "9fd18d88-6997-494a-bac6-f4996166b15e", "objectId": "decf17bb-d644-4eb8-821e-c578738b3082", "className": "2D_train", "geometry": {"points": [[215.62709625654094, 53.4079993383183], [158.5267011537117, 53.62114305308715], [151.1699117559335, 58.24109469168494], [150.91483764991762, 79.19799837255131], [163.29784681302075, 83.34905062262808], [173.26178034068562, 83.80351855519821], [195.46912552537503, 80.48103820012398], [195.2278216099351, 70.50365846294991], [215.41343786085795, 69.03561364470097], [215.62709625654094, 53.4079993383183]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "occlusion": "50-75%", "type": "regionalExpress"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "ea49b455-4b2c-49e9-9eb8-93a6360d4341", "objectId": "f24c55f7-2ff0-4137-9328-5ae022bfcb07", "className": "2D_train", "geometry": {"points": [[244.30329339815864, 49.494420868450625], [244.47916950238223, 66.35177630391422], [251.74754711251913, 72.15520888347997], [266.4279952950085, 72.52222008804219], [268.1749933416163, 56.16523521172498], [256.86139157780525, 49.93065428147774], [244.30329339815864, 49.494420868450625]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "50-75%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "a8da244f-31c6-43d7-8a1d-2e73b4a4f47a", "objectId": "af9f7ffb-9c2a-481c-beab-843cfa428cda", "className": "2D_signal_pole", "geometry": {"points": [[250.45201617458773, 50.71780189143546], [250.663243587605, 101.5647481301877], [253.91983037484323, 101.20177344344175], [253.9103725015183, 50.33069510086087], [250.45201617458773, 50.71780189143546]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "af37bdd7-42a8-4dda-938a-f01974eb3f38", "objectId": "369bd0f5-4f00-4fbf-8f12-23c8839ce0e0", "className": "2D_signal_pole", "geometry": {"points": [[272.0669288666686, 59.84162990166313], [270.1984671266271, 59.85450315012786], [270.3111834965438, 75.20745659580398], [272.1792569208072, 75.19540176869818], [272.37307959320896, 75.19005892285799], [272.2608467084985, 59.84370500029018], [272.0669288666686, 59.84162990166313]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "187f64ea-508f-4b8e-b35e-3d2f7c716494", "objectId": "2b692b4e-1950-4d24-a19e-2514bf0529bd", "className": "2D_signal_pole", "geometry": {"points": [[307.12808392483356, 52.728027304047885], [305.2504034131327, 52.74018468727546], [305.17166010392066, 52.74818710334694], [305.30279098057986, 71.82560962914359], [305.3815771712672, 71.8268528365567], [307.2587617104768, 71.81419580625321], [307.12808392483356, 52.728027304047885]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "34d335c9-7931-4beb-b4c8-2573f819dece", "objectId": "cd045b17-320a-43bc-9983-bd93415a4a78", "className": "2D_train_front", "geometry": {"points": [[252.01573441935713, 72.17784138140094], [251.94209865880663, 52.443457553866736], [262.25110513587674, 53.17981515937175], [267.9210586982653, 56.56706014469478], [266.66925076890675, 72.69329170525445], [252.01573441935713, 72.17784138140094]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "3d1eb564-f0db-4178-b457-ca5cdd9bde8e", "objectId": "0fdb210a-34dd-4cee-8684-fe1756bbe023", "className": "2D_signal_pole", "geometry": {"points": [[329.69952912896287, 50.55930329622437], [327.821831092544, 50.57161863603358], [327.7430865286424, 50.579628384710794], [327.87571336357024, 69.65740281894985], [327.954501456596, 69.65863893368864], [329.83170164252846, 69.64582521654027], [329.69952912896287, 50.55930329622437]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "ir_middle", "uri": "A0001781_image/000_1632321843.100464760.png", "timestamp": "1632321843.100464760"}}, {"id": "d57eceac-9803-47d6-a392-5f160460d24d", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "2D_catenary_pole", "geometry": {"points": [[260.47855474249775, 0.39588499833790314], [251.17422598271824, 432.9234603337311], [239.49789164276353, 457.3153107487254], [220.64272580444697, 479.9645771809027], [361.18059413447105, 480.5211708073802], [347.0891214596523, 445.80185811149374], [345.1637373377166, -0.32537194632184674], [260.47855474249775, 0.39588499833790314]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": true, "occlusion": "0-25%"}, "sensor": {"type": "ir_right", "uri": "A0001782_image/000_1632321843.100464620.png", "timestamp": "1632321843.100464620"}}, {"id": "2555948f-980f-460d-ad95-b83ab40ae811", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "2D_signal_pole", "geometry": {"points": [[3175.499278056425, 77.62871536173974], [3147.0569816667157, 977.8463340950655], [3138.133657285401, 981.546321212347], [3127.829201000016, 1054.7674006832856], [3099.1451750410156, 1079.2021026783707], [3206.5808255746283, 1080.0916394031585], [3231.9456231632303, 1069.449425025522], [3210.607115916319, 1063.9684518725182], [3199.5595453318815, 974.5791194612398], [3189.6243929255374, 974.2188286641388], [3220.196192881666, 77.04753740082552], [3175.499278056425, 77.62871536173974]]}, "attributes": {"isTruncatedTop": false, "isTruncatedBottom": false, "structure": "structured", "occlusion": "0-25%"}, "sensor": {"type": "new_left_rect", "uri": "S1213751_image/000_1632321843.100000003.png", "timestamp": "1632321843.100000003"}}, {"id": "49a31b51-46c0-4094-bbf8-df9435bfb7d5", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "2D_catenary_pole", "geometry": {"points": [[3161.3216711175187, 0.39071438968773214], [3009.006971487705, 1.3797708807904194], [2949.1911833070244, 1081.322186667464], [2936.4881750043696, 1217.6786549335786], [2882.7653786733886, 1300.0536093077492], [2890.823798123036, 1356.462545455279], [2933.8020351878204, 1376.160904109972], [3216.7420958643193, 1377.0562840488217], [3219.4282356808685, 1320.6473479012918], [3168.3915791664367, 1292.890569796952], [3134.3671414901487, 1204.2479558508335], [3161.3216711175187, 0.39071438968773214]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "776daba6-3e3a-4236-9346-eb34eda502bd", "objectId": "c8ef60c9-73b9-49cd-a2e8-14588b7c0be8", "className": "2D_catenary_pole", "geometry": {"points": [[2460.535571748438, -1.1102230246251565e-16], [2435.064153898073, 588.8845489443853], [2427.1419789280126, 689.1102375738657], [2411.792944729856, 730.3102767373376], [2418.659617923768, 742.4279353148295], [2441.2792472684196, 750.1024524139076], [2472.381237617315, 741.2161694570802], [2499.44400844038, 725.0592913537579], [2489.273083685772, 692.0742456247332], [2495.794479401337, -0.9158659817003919], [2460.535571748438, -1.1102230246251565e-16]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "74a911f3-0c7d-4a9f-9373-d12053dd7ff2", "objectId": "5029d606-305c-4276-9cb9-5ae2ef61d285", "className": "2D_catenary_pole", "geometry": {"points": [[2351.379911877427, 8.258029543525254], [2327.2599089471564, 590.8180487672728], [2365.8104205974614, 590.4289551087207], [2368.6195265068827, 308.05074388988817], [2369.3205342643532, 7.1489559217262], [2351.379911877427, 8.258029543525254]]}, "attributes": {"structure": "solid", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "51584afe-1823-4aef-acc9-bf5a44264e24", "objectId": "e32d3223-1292-4c78-b0cd-46ce6ad9a0bc", "className": "2D_catenary_pole", "geometry": {"points": [[2302.833870518539, 54.86842863607177], [2282.1751580680025, 566.2440663274072], [2312.224013635023, 567.7771590017908], [2315.8855226936666, 54.91870297442928], [2302.833870518539, 54.86842863607177]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "5d25a9e6-b556-447b-b2e1-a912aaf2c597", "objectId": "c305af21-7b22-402f-8cda-3d7b76e9d073", "className": "2D_catenary_pole", "geometry": {"points": [[2276.794308563372, 157.15285310220145], [2263.395953445272, 525.0447007622206], [2284.741970674504, 528.3616748386789], [2289.3936892563497, 156.99704189099288], [2276.794308563372, 157.15285310220145]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "fd82e8bc-6bda-49bc-a856-25fcce5ae039", "objectId": "0650ff28-41a6-4a5f-ac17-89a769d5ffad", "className": "2D_catenary_pole", "geometry": {"points": [[2233.5462450051723, 342.54137871623647], [2229.9114287099533, 488.4340963104706], [2246.803260768546, 488.8046005162621], [2248.1063543264677, 344.00933406763033], [2233.5462450051723, 342.54137871623647]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "1d010155-3e0f-4bcc-8ad4-1d487af9fbcb", "objectId": "e03bf406-e5fb-4b6b-8bfb-4388de4b1cf1", "className": "2D_catenary_pole", "geometry": {"points": [[2224.9002830684103, 343.0949753667184], [2220.797055477605, 476.46349344092516], [2229.209847062869, 474.05831339475594], [2230.833997993038, 468.0385362889514], [2235.2264259650246, 349.39188881426827], [2236.1139446572506, 342.8267208530416], [2224.9002830684103, 343.0949753667184]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "2c89194d-c278-4aa6-b77c-5d08be32af97", "objectId": "27c9e593-ff11-4b1c-9253-aff4706ddb5b", "className": "2D_catenary_pole", "geometry": {"points": [[2213.6684389743564, 345.7183962734416], [2208.4776777382954, 479.398732333697], [2221.088987182486, 470.63599619487826], [2221.329388307926, 450.2171673707418], [2222.451039164778, 345.469590953596], [2213.6684389743564, 345.7183962734416]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "4d813448-fa73-4a5a-94d7-65c79a48e08c", "objectId": "0da518da-8c14-4364-b494-646809641015", "className": "2D_catenary_pole", "geometry": {"points": [[2197.060247618329, 347.3263620106746], [2192.217735808335, 460.83356189552876], [2205.6201539714375, 461.50495187489605], [2205.3826858507173, 347.75680000179125], [2197.060247618329, 347.3263620106746]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "6917d6f6-a8c1-435b-9f65-19c1cc470b3a", "objectId": "2165789a-d0d5-42df-93b7-192189b558a2", "className": "2D_catenary_pole", "geometry": {"points": [[2168.528997114576, 463.14599972390846], [2179.499616129764, 459.04777040852105], [2180.958241573476, 347.9252443116278], [2172.3906243812958, 347.6935345603534], [2168.528997114576, 463.14599972390846]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "6e429973-8174-4842-8911-484c456aad25", "objectId": "e9b747e7-e01f-4209-b082-6986ce17ce2d", "className": "2D_catenary_pole", "geometry": {"points": [[2154.1176305225163, 348.5369275080564], [2150.1291352690214, 443.2016067142174], [2159.02448850767, 437.038015617857], [2161.2452872560857, 348.6129699974628], [2154.1176305225163, 348.5369275080564]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "20650b4d-becb-4883-8317-2e66444dd03a", "objectId": "bb1f0700-9c85-4560-a998-fb3f04eeb296", "className": "2D_catenary_pole", "geometry": {"points": [[2138.694639344945, 348.21889060381244], [2137.159956268065, 440.093420014168], [2145.02206010892, 438.87753623368087], [2146.426829249604, 348.32942888482455], [2138.694639344945, 348.21889060381244]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "c2c02cf7-bb56-414a-9bb0-3ac1c90dbb78", "objectId": "3c0bd8ef-d8ce-4e9d-90cf-59fdb64fce7c", "className": "2D_train_front", "geometry": {"points": [[1761.8103863711867, 397.58627900059406], [1748.5089927444635, 416.9783755613854], [1749.1433306906524, 445.10069117576097], [1753.532981400018, 452.8570029466407], [1793.2421688993263, 453.79297374885994], [1792.9042818820558, 394.4957659982409], [1761.8103863711867, 397.58627900059406]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "8820acd4-ab74-4faf-b610-191813c35d61", "objectId": "decf17bb-d644-4eb8-821e-c578738b3082", "className": "2D_train", "geometry": {"points": [[1850.8851209937593, 396.3158065409051], [1845.8330277800248, 393.18313028325866], [1838.3025485245273, 390.8651595936834], [1761.7218275853063, 397.3226497044466], [1749.2487136928537, 416.6472678582471], [1749.0533043646296, 444.75263261986134], [1753.1906253145128, 453.03237953568686], [1791.2176999996639, 454.1019917279261], [1851.3940794943223, 442.0810721415158], [1850.8851209937593, 396.3158065409051]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "50-75%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "c3d8936e-b6bf-41d7-b1a8-8597b79bddd9", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "2D_signal_pole", "geometry": {"points": [[359.538259994467, 158.0338436212924], [364.2861917548877, 924.1442350235656], [367.6154240860531, 1071.5652241151531], [354.98634441163097, 1073.3693783543565], [350.9682742094059, 1154.409812921063], [319.9360055663172, 1174.009140485119], [381.45611708682634, 1175.6424177821236], [383.089394383831, 1205.585834893876], [419.2662882883578, 1225.5086734896336], [442.4318028416673, 1228.9961428176093], [452.2314666236953, 1223.5518851609272], [454.95359545203644, 1178.364546610465], [457.13129851470933, 1162.031773640418], [430.4544359969664, 1156.0430902180674], [420.1103464492702, 1070.0238192424881], [409.76625690157397, 1068.9349677111518], [403.33643620609973, 558.2177056612384], [405.3362152964708, 155.82304426606285], [359.538259994467, 158.0338436212924]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "32b1c496-884d-40f5-8a2e-881191605a9f", "objectId": "af9f7ffb-9c2a-481c-beab-843cfa428cda", "className": "2D_signal_pole", "geometry": {"points": [[1952.812801141457, 383.23740479570677], [1953.6868326201968, 467.95600097837627], [1949.0177202631648, 468.1783396620445], [1949.0372574623295, 482.819277520456], [1961.0403577373318, 483.60265218665944], [1961.093687333148, 468.2413656999634], [1956.7663136216402, 467.9426967504117], [1956.7293177507577, 383.31103426974266], [1952.812801141457, 383.23740479570677]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "e5d2d8a0-3577-461d-b75d-f97a3bbd7b9e", "objectId": "369bd0f5-4f00-4fbf-8f12-23c8839ce0e0", "className": "2D_signal_pole", "geometry": {"points": [[1995.473190089172, 402.385831692978], [1995.3229181966692, 433.9762216267264], [1999.1459145633978, 433.9991582972469], [1999.5458025230832, 433.9931805796862], [1999.6980801977081, 402.4162872333341], [1999.2980697084083, 402.4070101665048], [1995.473190089172, 402.385831692978]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "01c7af60-01a5-4f25-b762-367757e8fdd3", "objectId": "0fdb210a-34dd-4cee-8684-fe1756bbe023", "className": "2D_signal_pole", "geometry": {"points": [[2116.6085494834365, 384.9464194831233], [2116.4500685386824, 384.96094201409693], [2116.2206360650357, 424.22596759522906], [2116.3789092553684, 424.23048184904286], [2120.221805659801, 424.25239847821723], [2120.453783549436, 384.96927180512665], [2116.6085494834365, 384.9464194831233]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "b805a7b3-c62c-4875-b7f0-eb92988467f5", "objectId": "f24c55f7-2ff0-4137-9328-5ae022bfcb07", "className": "2D_train", "geometry": {"points": [[1955.5417019593174, 425.28411561646215], [1963.2716206619928, 432.6703250390555], [1985.20125442574, 432.35855270848805], [1992.765084497648, 422.8840250135086], [1992.410292603328, 388.88423881658906], [1989.788318173949, 383.53522604363945], [1983.513255610033, 379.7383397289518], [1961.7090823003775, 379.9404703801594], [1961.7308477005781, 384.9000792389711], [1955.0647407892232, 384.6358109368824], [1955.5417019593174, 425.28411561646215]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "606977bc-894e-467a-9c8b-0a529b47b0a0", "objectId": "52e89909-b83c-48fe-8fa2-f7cc53a30f28", "className": "2D_train_front", "geometry": {"points": [[1955.2459135494394, 424.83995615389836], [1963.2537807897286, 432.637090045759], [1985.0646823520956, 432.637090045759], [1989.2793493206689, 425.8936228960417], [1988.8578826238115, 393.73039812669936], [1970.676872481212, 390.3095187520965], [1955.4373328386232, 393.4617226005362], [1955.2459135494394, 424.83995615389836]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "occlusion": "0-25%", "type": "regionalExpress"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "1819c462-be33-49e1-be47-dab495af8086", "objectId": "2b692b4e-1950-4d24-a19e-2514bf0529bd", "className": "2D_signal_pole", "geometry": {"points": [[2068.2983298784698, 383.6963462983226], [2070.603024393257, 384.0377825227355], [2071.115178729876, 431.155981491712], [2068.3460985066263, 430.98526337950557], [2068.2983298784698, 383.6963462983226]]}, "attributes": {"isTruncatedTop": false, "isTruncatedBottom": false, "structure": "solid", "occlusion": "0-25%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "7348a041-751a-4a34-8e94-bebfa5f07f64", "objectId": "deb1b1ce-a67e-45b6-9d27-a8f4c686fc83", "className": "2D_signal_pole", "geometry": {"points": [[1983.4621916646586, 363.01517739825175], [1983.5316908169596, 378.208559867431], [1985.8717746326101, 380.8165602675596], [1985.5053762339892, 363.0043188659464], [1983.4621916646586, 363.01517739825175]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "50-75%"}, "sensor": {"type": "new_middle_rect", "uri": "S1213752_image/000_1632321843.100000029.png", "timestamp": "1632321843.100000029"}}, {"id": "dff01b20-7660-4c5e-8e43-1f87ddeb84c0", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "2D_catenary_pole", "geometry": {"points": [[171.05932469243663, 1.1049542626990663], [177.5180319389281, 586.9263322272407], [168.56690880097162, 588.8321299278731], [167.87755439940221, 593.1404579423056], [176.71718850099478, 750.0439632455738], [181.45533808383928, 756.6824683635721], [186.41230217455458, 1083.1386809996147], [169.6204688841475, 1139.1828826464723], [155.46379993174136, 1144.5758993902461], [134.56586004961798, 1185.6976520615212], [105.99282202863137, 1186.7953996290837], [105.99282202863137, 1186.7953996290837], [105.55770328138306, 1238.9661003797], [147.3590595915058, 1316.7574230774912], [493.52900649555596, 1296.555049639185], [492.5081717137472, 1215.8659151642896], [482.4389105754769, 1201.3611471453269], [428.2724353582367, 1170.732590372043], [389.1287835165974, 1091.4416033082096], [328.2581061777954, 1.4893114044714075], [171.05932469243663, 1.1049542626990663]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "new_right_rect", "uri": "S1213755_image/000_1632321843.100000008.png", "timestamp": "1632321843.100000008"}}, {"id": "2c1c6858-7b50-40c8-86fe-adda56a23dfc", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "radar_catenary_pole", "geometry": {"points": [[1439.8360152542107, 1349.2173525736712], [1437.6979963389713, 1349.2905427154196], [1437.7930754279191, 1352.067978115298], [1439.9310943431585, 1351.9947879735496], [1439.8360152542107, 1349.2173525736712]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "f11d931e-f0cf-4160-ad11-109118fd20a0", "objectId": "c8ef60c9-73b9-49cd-a2e8-14588b7c0be8", "className": "radar_catenary_pole", "geometry": {"points": [[1440.8325573607628, 1206.6457597436624], [1438.498512817745, 1206.7411839383046], [1438.6082071714638, 1209.423572832615], [1440.9422517144815, 1209.3281486379728], [1440.8325573607628, 1206.6457597436624]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "68a18db8-7d94-4a3b-bd1e-7806e16ccba9", "objectId": "5029d606-305c-4276-9cb9-5ae2ef61d285", "className": "radar_catenary_pole", "geometry": {"points": [[1441.159828954818, 1077.7787671507328], [1438.3903961478536, 1077.8943546477062], [1438.5045708915802, 1080.629851797987], [1441.2740036985447, 1080.5142643010133], [1441.159828954818, 1077.7787671507328]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "a5355498-aa6b-4f42-83e7-e8ccc8eef7bb", "objectId": "e32d3223-1292-4c78-b0cd-46ce6ad9a0bc", "className": "radar_catenary_pole", "geometry": {"points": [[1438.675457584013, 975.6469676058257], [1438.6723265116416, 978.3219180861761], [1441.2686431819725, 978.3249474824191], [1441.2717742543439, 975.6499970020689], [1438.675457584013, 975.6469676058257]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "951b6b87-5eeb-4a65-9268-ec7e776ecae2", "objectId": "c305af21-7b22-402f-8cda-3d7b76e9d073", "className": "radar_catenary_pole", "geometry": {"points": [[1438.450028958643, 890.6810842234358], [1438.4032269690513, 893.3790921444261], [1441.2328313762835, 893.4281769210073], [1441.2796333658753, 890.7301690000171], [1438.450028958643, 890.6810842234358]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "dd4370f5-7ad6-47d5-a4c3-3a1eeb46de24", "objectId": "0650ff28-41a6-4a5f-ac17-89a769d5ffad", "className": "radar_catenary_pole", "geometry": {"points": [[1438.7846032470713, 703.6292833447095], [1438.6535575301843, 706.1475779715754], [1441.2241001270843, 706.2813425413782], [1441.3551458439713, 703.7630479145123], [1438.7846032470713, 703.6292833447095]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "0d3de5e4-79cb-44ed-8a9a-2e6fdcb861f4", "objectId": "e03bf406-e5fb-4b6b-8bfb-4388de4b1cf1", "className": "radar_catenary_pole", "geometry": {"points": [[1439.032751474781, 617.6850921513459], [1438.9724842421797, 620.6127165617786], [1441.7728165709043, 620.6703633962615], [1441.8330838035058, 617.7427389858287], [1439.032751474781, 617.6850921513459]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "07ad037f-53d5-497d-b438-c567f5e6a036", "objectId": "27c9e593-ff11-4b1c-9253-aff4706ddb5b", "className": "radar_catenary_pole", "geometry": {"points": [[1438.9351838479977, 502.8919585036949], [1438.9187453990753, 505.5081567300267], [1441.5551313625306, 505.5247220252571], [1441.5715698114532, 502.90852379892533], [1438.9351838479977, 502.8919585036949]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "f04beb0f-ed2d-4805-ba25-233e7c2c5f09", "objectId": "0da518da-8c14-4364-b494-646809641015", "className": "radar_catenary_pole", "geometry": {"points": [[1438.0246136084365, 360.93539776251896], [1438.023625544474, 363.47922596093053], [1440.6537142878606, 363.48024752988204], [1440.654702351823, 360.93641933147046], [1438.0246136084365, 360.93539776251896]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "ba5fc5e5-20b2-467e-8bad-59ccc925cbe5", "objectId": "2165789a-d0d5-42df-93b7-192189b558a2", "className": "radar_catenary_pole", "geometry": {"points": [[1435.555890820227, 195.3801691569613], [1435.477589095975, 197.94155830314253], [1438.0642417949564, 198.0206323347761], [1438.1425435192084, 195.4592431885951], [1435.555890820227, 195.3801691569613]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "419ec603-be77-4f3b-8d0a-8e7e8856bf34", "objectId": "e9b747e7-e01f-4209-b082-6986ce17ce2d", "className": "radar_catenary_pole", "geometry": {"points": [[1435.4388286177987, 12.308554035030284], [1432.856373226615, 12.329545178582293], [1432.877704168851, 14.953804625915836], [1435.4601595600348, 14.932813482363827], [1435.4388286177987, 12.308554035030284]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "7ee3ab51-692e-40f0-af97-4b6436f695fc", "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", "className": "radar_person", "geometry": {"points": [[1428.378582949802, 141.44139601889424], [1428.3767753687102, 143.19232087274622], [1430.3680955589964, 143.1943766278605], [1430.3699031400881, 141.44345177400874], [1428.378582949802, 141.44139601889424]]}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "age": "adult", "function": "worker", "pose": "upright"}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "7f91cdf6-31b3-4f05-a39d-f57a5b869471", "objectId": "87563e91-2b69-475b-9a8b-07f794d1bdb2", "className": "radar_person", "geometry": {"points": [[1417.313157999752, 140.03204520672944], [1417.1678673260371, 141.64153691848855], [1418.883982393948, 141.79645260557618], [1419.0292730676629, 140.18696089381706], [1417.313157999752, 140.03204520672944]]}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "function": "worker", "age": "adult", "pose": "upright"}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "725d0af2-1e89-475e-93d8-b6723658a670", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "radar_signal_pole", "geometry": {"points": [[1405.3035340107976, 1331.583243703627], [1405.1262873486369, 1335.483251093747], [1406.845894123657, 1335.5614033972206], [1407.0231407858178, 1331.6613960071006], [1405.3035340107976, 1331.583243703627]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "2a93ea65-df3c-471b-adab-b9cd84d911cc", "objectId": "af9f7ffb-9c2a-481c-beab-843cfa428cda", "className": "radar_signal_pole", "geometry": {"points": [[1407.7386742626543, 505.3344603894534], [1406.1137396785648, 505.34934061806985], [1406.1408327649128, 508.30793044431914], [1407.7657673490023, 508.2930502157027], [1407.7386742626543, 505.3344603894534]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "fb78d714-595e-4c01-8b83-f718a70fa8ea", "objectId": "decf17bb-d644-4eb8-821e-c578738b3082", "className": "radar_train", "geometry": {"points": [[1353.8969115814423, 0], [1353.6554862680664, 22.22337701347442], [1363.6346201502863, 22.33178607854461], [1363.8772231736245, 0], [1353.8969115814423, 0]]}, "attributes": {"type": "regionalExpress", "isTruncatedBack": false, "isTruncatedFront": false}, "sensor": {"type": "radar", "uri": "talker1_Nvt_Polar/000_1632321843.027192325.png", "timestamp": "1632321843.027192325"}}, {"id": "20739d81-832e-465b-8975-db57b2557231", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "2D_signal_pole", "geometry": {"points": [[1840.259739207403, 586.9888686883753], [1811.6729290924995, 587.0475065025781], [1808.6417538101127, 1159.8943985029953], [1802.9428286208936, 1159.5552483670008], [1788.089556181469, 1214.266036416236], [1760.5353391306076, 1220.190854327238], [1757.4814742921417, 1251.7308025257362], [1791.8518732877808, 1255.2724671796257], [1812.6584835879223, 1257.0927025915546], [1823.7926845489806, 1256.7516424984933], [1842.1159710272218, 1258.5117913977], [1861.7986096095499, 1255.93033267796], [1860.5531550914154, 1219.5467457883506], [1845.0172199964084, 1208.6663029254175], [1844.7939075397458, 1204.0047759317044], [1843.1519977575706, 1202.7857223707513], [1843.1850260130368, 1168.093093418962], [1835.9330201528128, 1167.7246516957823], [1840.259739207403, 586.9888686883753]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_left_rect", "uri": "S1206062_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "5fa498c1-a513-48a5-b4fc-97e9f7704492", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "2D_catenary_pole", "geometry": {"points": [[1893.4424824331306, 0], [1858.6798648464548, 1112.012344001047], [1829.4336397702912, 1154.1406460777318], [1869.3833931998035, 1177.436037211573], [1999.8136172985292, 1178.6165248942204], [1987.6324871337947, 1122.787343330701], [1980.2028768997639, 26.37574881710415], [1998.8096351893048, 12.307224256719516], [2000.7497118910667, 0], [1893.4424824331306, 0]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "259463c8-350b-4e73-b1f4-c829497b4ba2", "objectId": "c8ef60c9-73b9-49cd-a2e8-14588b7c0be8", "className": "2D_catenary_pole", "geometry": {"points": [[1508.0743904136455, 293.92391722087376], [1489.8470865116817, 954.2570966218138], [1482.7593160062936, 966.701407390839], [1491.8602333751983, 968.4488360501778], [1540.880546724689, 969.1809374899037], [1534.4968739958802, 943.1188026151603], [1531.5137972675275, 294.19891781616553], [1508.0743904136455, 293.92391722087376]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "e240a423-0461-4eae-8e4e-b0d5d7b40149", "objectId": "5029d606-305c-4276-9cb9-5ae2ef61d285", "className": "2D_catenary_pole", "geometry": {"points": [[1429.7723897481976, 496.662619237473], [1417.9709868701955, 909.093230376385], [1411.5320753476578, 918.8263310683585], [1416.5166950259477, 923.366707442783], [1453.1151543533545, 923.5737291352001], [1447.7000020918176, 908.7124989997703], [1444.9398390080244, 495.9702542972437], [1429.7723897481976, 496.662619237473]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "0b3e2983-709b-4811-9109-a0599dfd9245", "objectId": "e32d3223-1292-4c78-b0cd-46ce6ad9a0bc", "className": "2D_catenary_pole", "geometry": {"points": [[1396.8963992679667, 575.2184478222009], [1387.198110771021, 898.5691645550961], [1382.1182582713577, 902.6199204767894], [1386.9727077395023, 905.8678710475407], [1413.5115792061276, 906.0784040648047], [1410.4710006527853, 895.4287780368143], [1408.0807642985515, 575.0495539174674], [1396.8963992679667, 575.2184478222009]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "d33935a9-69ca-4f27-930f-af988de9204b", "objectId": "c305af21-7b22-402f-8cda-3d7b76e9d073", "className": "2D_catenary_pole", "geometry": {"points": [[1380.5858822870807, 641.6983697593024], [1372.9173806407257, 886.956254705121], [1366.1184506059813, 893.757962795325], [1368.5448170045456, 896.1797661282585], [1368.5815624890067, 896.4323976298501], [1387.1441804742021, 896.1342500068392], [1388.7504025134258, 847.8864898017126], [1389.6163678671542, 641.3912580216855], [1380.5858822870807, 641.6983697593024]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "b29c606f-f052-4757-bc73-8caec454dabc", "objectId": "0650ff28-41a6-4a5f-ac17-89a769d5ffad", "className": "2D_catenary_pole", "geometry": {"points": [[1353.1866786531384, 772.3198800118771], [1351.4303042770193, 878.6133410441508], [1347.7114559198728, 881.7687275290026], [1349.5274529851645, 883.5364012967401], [1365.905690343086, 883.4809782497316], [1366.4438254135714, 883.3489892452908], [1363.8635282997197, 877.1937206689901], [1362.369848189636, 772.2009869317994], [1353.1866786531384, 772.3198800118771]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "a9873243-bf1d-4ccf-bb48-822a4c1e7a42", "objectId": "e03bf406-e5fb-4b6b-8bfb-4388de4b1cf1", "className": "2D_catenary_pole", "geometry": {"points": [[1346.7184344258512, 772.4779383894455], [1343.2037609415133, 873.0914146956605], [1340.7245287034157, 877.1483401761841], [1344.1426523685234, 879.5989168060853], [1351.4071052354504, 878.6390021605228], [1353.243014221133, 772.3708649789136], [1346.7184344258512, 772.4779383894455]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "fb4e7f17-9e7c-4567-99e6-eea9815783c1", "objectId": "27c9e593-ff11-4b1c-9253-aff4706ddb5b", "className": "2D_catenary_pole", "geometry": {"points": [[1346.639018335579, 772.3563863874089], [1338.8878261122436, 772.5878890111999], [1335.8126380447975, 869.7355268460674], [1333.1985057507547, 873.2210365714581], [1335.995231596975, 875.3798050805625], [1336.0677001204238, 875.462194645895], [1343.2241442509455, 873.6698502425534], [1346.639018335579, 772.3563863874089]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "014a9a43-5678-475a-bac1-7ef6ab2b9462", "objectId": "0da518da-8c14-4364-b494-646809641015", "className": "2D_catenary_pole", "geometry": {"points": [[1334.7201642209902, 773.1489791986281], [1327.3368229430737, 772.9475646277244], [1325.7978766523484, 866.784206216471], [1324.4659173654507, 871.2677166062334], [1324.9344442617685, 871.6047910468338], [1334.4380235586611, 871.1920195678885], [1334.7201642209902, 773.1489791986281]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "3c4d1086-f870-436d-9cc2-6f9a41017c90", "objectId": "2165789a-d0d5-42df-93b7-192189b558a2", "className": "2D_catenary_pole", "geometry": {"points": [[1311.3993150654285, 773.1158531759936], [1309.5915561717518, 868.2548236758764], [1318.8212838757086, 867.8958336503314], [1319.511520405738, 773.2738906259249], [1311.3993150654285, 773.1158531759936]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "25-50%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "5697bb72-eb9c-4bd3-ae78-3a1044e8803c", "objectId": "e9b747e7-e01f-4209-b082-6986ce17ce2d", "className": "2D_catenary_pole", "geometry": {"points": [[1305.011478225215, 773.3600779296899], [1299.2840543664431, 773.5129261735873], [1298.0695224753613, 862.1642920459913], [1297.3760010832505, 865.3256104739148], [1305.9078399853706, 865.4302321086479], [1305.6010459028425, 861.050047335264], [1305.011478225215, 773.3600779296899]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "b79aa3b4-9199-422d-bb75-8b2d3cd37b24", "objectId": "bb1f0700-9c85-4560-a998-fb3f04eeb296", "className": "2D_catenary_pole", "geometry": {"points": [[1295.6368893813826, 773.4737139298494], [1290.0979573973445, 773.6757219432446], [1288.44136710039, 859.6853786568572], [1289.724896708166, 861.5513179486206], [1295.9806358984758, 861.8820928760795], [1295.6368893813826, 773.4737139298494]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "59ef72ab-9075-4118-a7cc-24616bc35cde", "objectId": "66a022bf-5710-4c1e-8ac0-bbe5094700e7", "className": "2D_train_front", "geometry": {"points": [[1072.9385304996163, 820.6029811455239], [1050.4718558615953, 820.4027927827685], [1043.7147226410966, 827.2867279960363], [1044.4540876875944, 850.8642578121305], [1049.39820868585, 855.1893767293404], [1072.4197272011731, 854.3768664434587], [1072.9385304996163, 820.6029811455239]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "b1645d0d-98b1-48ee-a8c9-d17b6fcf1efe", "objectId": "decf17bb-d644-4eb8-821e-c578738b3082", "className": "2D_train", "geometry": {"points": [[1072.6689459498184, 820.1747921666355], [1050.3523714120151, 820.4913079727004], [1043.9974463072183, 827.1887587012947], [1044.7784866599316, 851.0826168789723], [1049.1892933799331, 855.3816921052442], [1102.8606122685214, 852.9492972403657], [1102.9657291431022, 834.0951862823231], [1113.2977758290815, 833.597256321553], [1113.6284876793097, 820.1066873895556], [1072.6689459498184, 820.1747921666355]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "50-75%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "2a394882-830d-467c-a40c-b608576574b5", "objectId": "f24c55f7-2ff0-4137-9328-5ae022bfcb07", "className": "2D_train", "geometry": {"points": [[1180.2118335044754, 810.5355474241367], [1162.7581072051173, 809.6917479823076], [1162.861055244903, 836.7134372428326], [1173.7164684966679, 841.3222845884613], [1174.5326199885967, 847.0353450319627], [1192.691990684012, 846.1171746035429], [1196.6842597953553, 842.9733566430691], [1197.078804953129, 822.5508002740993], [1195.6525647842336, 818.1074673588365], [1180.2118335044754, 810.5355474241367]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "25-50%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "a11b3ed6-1fd0-4f01-905b-add81b9ac278", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "2D_signal_pole", "geometry": {"points": [[228.66296943462015, 486.47137136167777], [201.9082497217064, 487.5939410164567], [197.85806586618497, 1014.1091048537821], [191.0507152724503, 1015.9243983454448], [161.81016088694605, 1131.3157288916213], [212.8342371724013, 1133.9184753035122], [216.46482415572646, 1167.50140489927], [230.39191376692142, 1168.0511223460135], [281.44722787021277, 1154.9685080629972], [271.11484727694494, 1109.531988235108], [242.84828615744928, 1103.5811332625826], [243.59214302901495, 1035.5182295143231], [230.94657621239847, 1029.195446106015], [242.98583524516027, 499.6333156130868], [228.66296943462015, 486.47137136167777]]}, "attributes": {"structure": "structured", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "460a9a22-dcc2-4a69-b30c-f8ac34147443", "objectId": "af9f7ffb-9c2a-481c-beab-843cfa428cda", "className": "2D_signal_pole", "geometry": {"points": [[1174.5201235779195, 805.6413131288666], [1174.0986185622764, 863.024831660618], [1172.7726088699412, 874.8375648473062], [1179.5725487393338, 874.7113980121052], [1178.3626803810962, 862.7740044948051], [1178.2266272486443, 805.6539068430958], [1174.5201235779195, 805.6413131288666]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "ff25a847-6cbd-48e6-9a8d-b0b46b348bfc", "objectId": "369bd0f5-4f00-4fbf-8f12-23c8839ce0e0", "className": "2D_signal_pole", "geometry": {"points": [[1200.839457398577, 827.1698696378571], [1200.9146590862135, 846.8777719022014], [1202.6704232185214, 846.9594214731007], [1202.6412082104782, 827.1219885063941], [1200.839457398577, 827.1698696378571]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "ffe1367a-b06d-4f19-a41e-552c266eb67e", "objectId": "0fdb210a-34dd-4cee-8684-fe1756bbe023", "className": "2D_signal_pole", "geometry": {"points": [[1247.0668039463897, 816.077653590104], [1244.8856102492666, 816.050415878094], [1244.7770882102332, 841.1700414984541], [1247.1617559968734, 841.2594590709483], [1247.0668039463897, 816.077653590104]]}, "attributes": {"structure": "solid", "isTruncatedTop": false, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "840490c7-1173-43bf-83ad-a2dfe271bcac", "objectId": "d6851e35-b38f-4cf7-8f82-6f06f68aa1d4", "className": "2D_train_front", "geometry": {"points": [[1175.9219196013369, 846.7175436160134], [1173.6216727900107, 841.1312299313639], [1173.7038244618439, 823.8793788464169], [1179.4544414901595, 820.3468569575945], [1192.3522539679532, 820.4290086294276], [1196.7884442469394, 823.96153051825], [1196.7884442469394, 843.5957800863563], [1192.5987089834523, 846.2246335850149], [1175.9219196013369, 846.7175436160134]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "d90bb76b-f337-4685-829e-1ba2225c819a", "objectId": "3c0bd8ef-d8ce-4e9d-90cf-59fdb64fce7c", "className": "rgb_train_front", "geometry": {"points": [[1032.7564941648475, 747.4876390993209], [1025.5478894994296, 754.632663325361], [1027.6673629744796, 764.9126972541657], [1030.9139595433585, 767.7306314423582], [1056.0484706582115, 767.8946663065118], [1052.185820996805, 746.2196649083835], [1032.7564941648475, 747.4876390993209]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "type": "regionalExpress", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "c41eb197-3979-4c3f-b59a-c09064e46152", "objectId": "7c14c7ad-9feb-47a3-ab8a-900b5fd3b8ab", "className": "rgb_train_front", "geometry": {"points": [[1156.515907286252, 756.5792368064997], [1162.0624564008249, 759.3936355712935], [1175.8285338773094, 759.2957899030495], [1178.0606383720615, 756.8117668582964], [1175.754832730991, 745.0632667390316], [1164.0705125143288, 743.895514338119], [1154.6569095449531, 745.114694681854], [1156.515907286252, 756.5792368064997]]}, "attributes": {"isTruncatedBack": false, "isTruncatedFront": false, "occlusion": "0-25%", "type": "regionalExpress"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "5a9b494a-f4e9-4c74-b7a4-b4ab386c68fa", "objectId": "82d92a87-bd0e-4c69-bc10-53926b40c7be", "className": "2D_signal_pole", "geometry": {"points": [[906.2350607180429, 894.6548179292444], [906.2350607180429, 883.6813745992408], [910.807328772211, 883.4527611965324], [911.2645555776278, 811.6681527460926], [915.6082102290875, 811.4395393433841], [915.8368236317959, 883.2241477938239], [919.5449764694055, 883.4527611965324], [919.7232514778389, 894.8330929376777], [906.2350607180429, 894.6548179292444]]}, "attributes": {"isTruncatedTop": false, "isTruncatedBottom": false, "structure": "solid", "occlusion": "0-25%"}, "sensor": {"type": "rgb_middle_rect", "uri": "S1206063_image/000_1632321843.100000064.png", "timestamp": "1632321843.100000064"}}, {"id": "30a024aa-1012-49cb-8865-e2606d035e27", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "2D_catenary_pole", "geometry": {"points": [[66.51462399122317, 0], [75.79216260590809, 12.762761285326386], [51.26148437989676, 1154.4692403477159], [17.126272200689662, 1199.605467125662], [15.892545424294273, 1222.9151708184852], [56.39501786410631, 1255.791825491923], [242.65672996415782, 1247.3969234756282], [241.0830834447857, 1218.9545261157598], [206.37589510102717, 1206.1320913955078], [182.1347690792257, 1142.9065395710345], [158.2322835266631, 1.1102230246251565e-16], [66.51462399122317, 0]]}, "attributes": {"structure": "structured", "isTruncatedTop": true, "isTruncatedBottom": false, "occlusion": "0-25%"}, "sensor": {"type": "rgb_right_rect", "uri": "S1206064_image/000_1632321843.100000040.png", "timestamp": "1632321843.100000040"}}], "3D_BOUNDING_BOX": [{"id": "13478f94-d556-4f64-a72b-47662e94988e", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "3D_catenary_pole", "geometry": {"size": {"height": 12.887274595406309, "width": 0.9726718154765793, "length": 0.7487449536720978}, "center": {"x": 27.075067129430366, "y": -3.7850908693727328, "z": 6.671744016124283}, "quaternion": {"x": -7.710484478135359e-17, "y": -7.978951661384013e-17, "z": 0.7191010536652217, "w": 0.6949055148849863}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "f4241163-2e06-4bea-ba41-1e1c53a0d205", "objectId": "c8ef60c9-73b9-49cd-a2e8-14588b7c0be8", "className": "3D_catenary_pole", "geometry": {"size": {"height": 11.32591768831262, "width": 0.939620949660413, "length": 0.8177564977894478}, "center": {"x": 76.98025553964469, "y": -3.9908722013536506, "z": 5.664758071751996}, "quaternion": {"x": 0.007288131704766536, "y": -0.0066217671626138, "z": 0.7213715704319794, "w": 0.6924782254399036}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "af8bc1d1-fd49-4c06-9dca-f2ba7ebc7e0c", "objectId": "5029d606-305c-4276-9cb9-5ae2ef61d285", "className": "3D_catenary_pole", "geometry": {"size": {"height": 11.387714043893492, "width": 0.958257601454503, "length": 0.9701948653735709}, "center": {"x": 122.07535013383624, "y": -4.08383238353245, "z": 5.694251848572822}, "quaternion": {"x": 0.00369050768501779, "y": -0.003449208351798712, "z": 0.7216906047044083, "w": 0.6921973376112901}}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "1d158fb8-2d57-4a3d-94b5-07acccd5aefd", "objectId": "e32d3223-1292-4c78-b0cd-46ce6ad9a0bc", "className": "3D_catenary_pole", "geometry": {"size": {"height": 11.381109367137512, "width": 0.9362333565610912, "length": 0.9087736685183327}, "center": {"x": 157.85679712362287, "y": -4.123633777940086, "z": 5.689919294379574}, "quaternion": {"x": 0.004022454964914071, "y": -0.004251513903410736, "z": 0.7066813725292935, "w": 0.7075077258982393}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "36773490-19a5-4894-be95-ffd3ae1eba3f", "objectId": "c305af21-7b22-402f-8cda-3d7b76e9d073", "className": "3D_catenary_pole", "geometry": {"size": {"height": 10.49502347160944, "width": 0.9444448385557216, "length": 0.9905608549058221}, "center": {"x": 187.58179664895056, "y": -4.09161786056079, "z": 5.247033517550782}, "quaternion": {"x": 0.00353253715396303, "y": -0.0035943470674366512, "z": 0.7009389355960713, "w": 0.7132034845791636}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "a7c91545-2bb1-4342-94b3-cb2e638f0d5a", "objectId": "0650ff28-41a6-4a5f-ac17-89a769d5ffad", "className": "3D_catenary_pole", "geometry": {"size": {"height": 9.582128549471806, "width": 0.8825956882587561, "length": 0.9009072204283313}, "center": {"x": 253.06564046993464, "y": -4.201523090477218, "z": 4.791064274735957}, "quaternion": {"x": -6.154728725788275e-16, "y": 4.872960476144086e-16, "z": 0.6884884999636752, "w": 0.7252472581249572}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "0bfdf09d-7f99-4dae-a51a-edfea7a2a1e3", "objectId": "e03bf406-e5fb-4b6b-8bfb-4388de4b1cf1", "className": "3D_catenary_pole", "geometry": {"size": {"height": 9.122189306988608, "width": 1.0248856333348373, "length": 0.9803239657538195}, "center": {"x": 283.0877952791687, "y": -4.340974407994947, "z": 4.561094653494363}, "quaternion": {"x": -7.190320005376218e-16, "y": 6.943797625527996e-16, "z": 0.6997923436831516, "w": 0.7143463275767867}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "e0d5d414-358a-4c9c-82a1-dbd489a9cc8c", "objectId": "27c9e593-ff11-4b1c-9253-aff4706ddb5b", "className": "3D_catenary_pole", "geometry": {"size": {"height": 9.166395843538007, "width": 0.915687454485901, "length": 0.9227533019559514}, "center": {"x": 323.32708090743336, "y": -4.285805161842478, "z": 4.5831979217690755}, "quaternion": {"x": -8.244463681442386e-16, "y": -8.19282370258064e-16, "z": 0.7048818303081658, "w": 0.7093247530584353}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "47135a62-ec1d-434d-aa3a-f90d41217e35", "objectId": "0da518da-8c14-4364-b494-646809641015", "className": "3D_catenary_pole", "geometry": {"size": {"height": 9.37049688364681, "width": 0.8903399366055378, "length": 0.9205311296242478}, "center": {"x": 373.02726207382983, "y": -3.9687073818519916, "z": 4.685248441823489}, "quaternion": {"x": -1.7061176000032221e-15, "y": -1.5484762827801308e-15, "z": 0.7069694420116025, "w": 0.7072440936917066}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "6f900dfc-f616-484e-a084-8ff2ec190eeb", "objectId": "2165789a-d0d5-42df-93b7-192189b558a2", "className": "3D_catenary_pole", "geometry": {"size": {"height": 9.776664883398379, "width": 0.8969049983018594, "length": 0.905751372471689}, "center": {"x": 430.9548597389459, "y": -3.0835232076570875, "z": 4.888332441699285}, "quaternion": {"x": -7.965776958244105e-17, "y": -7.725984220867584e-17, "z": 0.6962198832179828, "w": 0.7178285827493486}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "24bbde15-c191-4123-99d4-6886942104fc", "objectId": "e9b747e7-e01f-4209-b082-6986ce17ce2d", "className": "3D_catenary_pole", "geometry": {"size": {"height": 10.407967291307775, "width": 0.9185211485654554, "length": 0.903889245569542}, "center": {"x": 495.0290872343344, "y": -2.155393237663728, "z": 5.203983645653888}, "quaternion": {"x": 0.0, "y": 0.0, "z": 0.709974681893895, "w": 0.7042272013133707}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "daf6c303-3afa-4e3d-869d-f3e6ad8309cf", "objectId": "bb1f0700-9c85-4560-a998-fb3f04eeb296", "className": "3D_catenary_pole", "geometry": {"size": {"height": 10.695883613948222, "width": 0.9155471412683369, "length": 0.9164999475445299}, "center": {"x": 558.1598656891119, "y": -1.3400174508002798, "z": 5.520953609346796}, "quaternion": {"x": 0.0, "y": 0.0, "z": 0.7076205615691006, "w": 0.7065926272221008}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "817ed6c3-8552-42dd-b858-00bfa37ab8fa", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "3D_signal_pole", "geometry": {"size": {"height": 4.7988247505808035, "width": 0.6024836240895444, "length": 1.3664115660093574}, "center": {"x": 33.04968675735167, "y": 7.673850076470425, "z": 2.215776549933256}, "quaternion": {"x": -1.1110812999709735e-16, "y": -2.5183074152334238e-18, "z": 0.9997421784990658, "w": 0.022706310355101483}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "6cdb49ee-118b-46c4-a17b-ab40bc4544a6", "objectId": "418cad49-3c39-4fce-ae3e-c4b6db04499d", "className": "3D_catenary_pole", "geometry": {"size": {"height": 11.573852501573896, "width": 0.9344866445474989, "length": 0.922698681194263}, "center": {"x": -28.19481220330679, "y": -3.8022877825654664, "z": 5.786926250786943}, "quaternion": {"x": -7.875897163939664e-17, "y": -7.824608558471171e-17, "z": 0.7047931256242187, "w": 0.709412891109856}}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "059f694f-3215-41ba-94b0-74a9cd42b898", "objectId": "af9f7ffb-9c2a-481c-beab-843cfa428cda", "className": "3D_signal_pole", "geometry": {"size": {"height": 4.850638266050393, "width": 0.5687509502517719, "length": 1.0355498563207401}, "center": {"x": 322.4125816040898, "y": 7.371086270175778, "z": 2.425319133025268}, "quaternion": {"x": -1.1102579350230775e-16, "y": 5.083023179483528e-19, "z": 0.9999895182832635, "w": -0.004578572223589586}}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "9955441a-e315-49f7-85b4-cd8f1b3d0ba0", "objectId": "369bd0f5-4f00-4fbf-8f12-23c8839ce0e0", "className": "3D_signal_pole", "geometry": {"size": {"height": 2.7345535172334916, "width": 0.33526370122502774, "length": 0.30661435304516976}, "center": {"x": 631.3371213354337, "y": 10.956067406496267, "z": 3.392726206831182}, "quaternion": {"x": 0.0, "y": 0.0, "z": 0.9988417079216503, "w": 0.04811696702994255}}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "e7cbd2d8-16e4-46e0-9717-3c72d3c07996", "objectId": "0fdb210a-34dd-4cee-8684-fe1756bbe023", "className": "3D_signal_pole", "geometry": {"size": {"height": 3.560437779695704, "width": 0.35103706460382006, "length": 0.3207473820126947}, "center": {"x": 661.3799851877856, "y": 5.1146951761514226, "z": 4.615285122469505}, "quaternion": {"x": -4.800271803484849e-17, "y": -2.3540158457003534e-15, "z": 0.9996413941137189, "w": -0.02677840873503449}}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "72869011-d5c7-4857-8fc9-d4eb8ecba975", "objectId": "decf17bb-d644-4eb8-821e-c578738b3082", "className": "3D_train", "geometry": {"size": {"height": 4.388681071807479, "width": 3.4929029519399566, "length": 124.19055690880346}, "center": {"x": 554.1041190314115, "y": 23.599589571325012, "z": 2.93090209799623}, "quaternion": {"x": -0.0023581123241454573, "y": 1.2808386647010278e-05, "z": 0.9999824686485337, "w": 0.005431531809378548}}, "attributes": {"type": "regionalExpress"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "7bf23c73-a99b-4389-ac43-d3496cc34fff", "objectId": "f24c55f7-2ff0-4137-9328-5ae022bfcb07", "className": "3D_train", "geometry": {"size": {"height": 4.601779463996735, "width": 3.076443460527172, "length": 99.78402437119297}, "center": {"x": 759.509779717632, "y": 16.853355448605836, "z": 5.033459101870372}, "quaternion": {"x": -9.987652734909354e-05, "y": -0.011218637362343636, "z": 0.025316665088441877, "w": 0.9996165258083777}}, "attributes": {"type": "regionalExpress"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "875b494e-dbf6-4bb2-b5e6-d70b65fb5dbe", "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", "className": "3D_person", "geometry": {"size": {"height": 2.630954167344089, "width": 0.6128240254104295, "length": 0.6969624379982348}, "center": {"x": 449.98873978681786, "y": -0.4806687390397098, "z": 1.794378919126175}, "quaternion": {"x": 1.5745208333913983e-15, "y": 3.1747009348730734e-16, "z": 0.706741693498347, "w": 0.7074716804728571}}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "age": "adult", "function": "worker", "pose": "upright"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "ee6e9de9-8854-4467-9f5c-a95073ae24ad", "objectId": "87563e91-2b69-475b-9a8b-07f794d1bdb2", "className": "3D_person", "geometry": {"size": {"height": 2.5076941810839717, "width": 0.5656126646266026, "length": 0.6030825814604774}, "center": {"x": 450.4800128828465, "y": 3.4655004311025137, "z": 1.6427163119620496}, "quaternion": {"x": 4.991145492370739e-16, "y": 6.058734340669616e-16, "z": 0.7382091783501197, "w": -0.6745718708926732}}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "function": "worker", "age": "adult", "pose": "upright"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "b44b68c1-4e80-46f1-b822-00f453b1c227", "objectId": "e07310f7-02df-4d05-ac3c-6296117d27ea", "className": "3D_track", "geometry": {"size": {"height": 0.4700600559062381, "width": 1.7711582211586314, "length": 122.27286466409521}, "center": {"x": 61.44839751510897, "y": -0.11227183531167707, "z": -0.13687736210234927}, "quaternion": {"x": 9.864317113664832e-20, "y": -1.1102244878784408e-16, "z": -0.0009622825642576772, "w": 0.9999995370060261}}, "attributes": {"trackId": "ego_track"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "23b469d0-d2c0-4158-9794-29442f5288c1", "objectId": "fa0f2957-29a5-404c-95f1-45c44ff58103", "className": "3D_track", "geometry": {"size": {"height": 0.47775738489026043, "width": 1.819192891169864, "length": 127.96014613884917}, "center": {"x": 46.84626713678596, "y": 4.685932623267282, "z": -0.20159324395631406}, "quaternion": {"x": 7.014288024687007e-16, "y": -1.111171138321498e-16, "z": -0.00013516746707870067, "w": 0.9999999908648778}}, "attributes": {"trackId": "left_first_track"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "42116b2f-48f4-423e-9219-a8c6961ede1a", "objectId": "03a3625e-9082-41ec-8b44-24e92b7286eb", "className": "3D_track", "geometry": {"size": {"height": 0.612889360533506, "width": 1.846216879806486, "length": 137.81025130113468}, "center": {"x": 52.05160121842591, "y": 10.166373407059307, "z": 0.07494369318203808}, "quaternion": {"x": 1.9652476270442384e-20, "y": -1.1102230768065455e-16, "z": -0.00017701378071000032, "w": 0.9999999843330606}}, "attributes": {"trackId": "left_second_track"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "cff15e77-6e63-4d8c-b67b-ac45c96cd684", "objectId": "e4366b9f-a350-42a9-8ac2-61eeeee5513c", "className": "3D_signal_pole", "geometry": {"size": {"height": 4.212558513580023, "width": 1.1643567979785625, "length": 0.8081109969363183}, "center": {"x": 204.02671957014238, "y": 16.30381415441869, "z": 1.9281309299048264}, "quaternion": {"x": 1.080383758932362e-15, "y": 6.401082754169111e-16, "z": 0.7179686714245058, "w": -0.6960754175036853}}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}], "3D_SEGMENTATION": [{"id": "13478f94-d556-4f64-a72b-47662e94988e", "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [39814, 39815, 39816, 39817, 39818, 39819, 39820, 39821, 39822, 39823, 39824, 39825, 39826, 39827, 39828, 39829, 39830, 39831, 39832, 39833, 39834, 39835, 39836, 39837, 39838, 39839, 39840, 39841, 39842, 39843, 39844, 39845, 39846, 39847, 39848, 39849, 39850, 39851, 39852, 39853, 39855, 39858, 39859, 39861, 39864, 39867, 39870, 39871, 39873, 39875, 39876, 39877, 39879, 39881, 39882, 39883, 39884, 39885, 39886, 39887, 39888, 39889, 39890, 39891, 39892, 39893, 39894, 39895, 39896, 39897, 39898, 39899, 39900, 39901, 39902, 39903, 39904, 39905, 39906, 39907, 39908, 39909, 39910, 39911, 39912, 39913, 39914, 40184, 40185, 40186, 40187, 40189, 40190, 40191, 40192, 40193, 40194, 40195, 40196, 40197, 40198, 40199, 40200, 40201, 40202, 40203, 40204, 40205, 40206, 40207, 40208, 40209, 40210, 40211, 40212, 40213, 40214, 40215, 40216, 40217, 40218, 40220, 40221, 40222, 40223, 40224, 40226, 40227, 40228, 40229, 40230, 40232, 40233, 40234, 40235, 40237, 40238, 40239, 40240, 40241, 40242, 40243, 40244, 40245, 40246, 40247, 40248, 40249, 40250, 40251, 40252, 40253, 40254, 40255, 40256, 40257, 40258, 40259, 40260, 40261, 42607, 42608, 42609, 42610, 42611, 42612, 42613, 42614, 42615, 42616, 42617, 42618, 42619, 42620, 42621, 42622, 42623, 42624, 42625, 42626, 42627, 42628, 42629, 42630, 42631, 42632, 42633, 42634, 42635, 42636, 42637, 42638, 42640, 42641, 42643, 42644, 42646, 42647, 42649, 42650, 42652, 42653, 42655, 42656, 42657, 42658, 42659, 42661, 42663, 42664, 42665, 42666, 42667, 42668, 42669, 42670, 42671, 42672, 42673, 42674, 42675, 42676, 42677, 42678, 42679, 42680, 42681, 42682, 42683, 42684, 42685, 42686, 42687, 42688, 42689, 42690, 42691, 42692, 42693, 42696, 42697, 43790, 43791, 43792, 43793, 43794, 43795, 43796, 43797, 43798, 43799, 43800, 43801, 43802, 43803, 43804, 43805, 43806, 43807, 43808, 43809, 43810, 43811, 43812, 43813, 43814, 43815, 43816, 43817, 43818, 43819, 43820, 43821, 43822, 43823, 43824, 43825, 43827, 43828, 43829, 43830, 43834, 43835, 43836, 43840, 43841, 43846, 43847, 43850, 43852, 43853, 43854, 43855, 43856, 43857, 43858, 43859, 43860, 43861, 43862, 43863, 43864, 43865, 43866, 43867, 43868, 43869, 43870, 43871, 43872, 43873, 43874, 43875, 43876, 43877, 43878, 43879, 43880, 43881, 43882, 43883, 44264, 44265, 44266, 44267, 44268, 44269, 44270, 44271, 44272, 44273, 44274, 44275, 44276, 44277, 44278, 44279, 44280, 44281, 44282, 44283, 44284, 44285, 44286, 44287, 44288, 44289, 44290, 44291, 44292, 44294, 44295, 44296, 44297, 44298, 44300, 44301, 44302, 44303, 44304, 44306, 44307, 44308, 44309, 44310, 44312, 44313, 44314, 44315, 44316, 44317, 44318, 44319, 44320, 44321, 44322, 44323, 44324, 44325, 44326, 44327, 44328, 44329, 44330, 44331, 44332, 44333, 44334, 44335, 44336, 44337, 46721, 46722, 46723, 46724, 46725, 46726, 46727, 46728, 46729, 46730, 46731, 46732, 46733, 46734, 46735, 46736, 46737, 46738, 46739, 46740, 46741, 46742, 46743, 46744, 46745, 46746, 46747, 46748, 46749, 46750, 46751, 46752, 46753, 46754, 46755, 46756, 46757, 46758, 46760, 46764, 46765, 46766, 46771, 46772, 46777, 46778, 46783, 46784, 46785, 46789, 46790, 46791, 46795, 46796, 46797, 46798, 46799, 46800, 46801, 46802, 46803, 46804, 46805, 46806, 46807, 46808, 46809, 46810, 46811, 46812, 46813, 46814, 46815, 46816, 46817, 46818, 46819, 46820, 46822, 46823, 46824, 46825, 46826, 46828, 46829, 46830, 46831, 46832, 46834, 46835, 46836, 46837, 46838, 46840, 46841, 46842, 46843, 46844, 46846, 46847, 46848, 46849, 46850, 46853, 46854, 46855, 46856, 46859, 46860, 46865, 46866, 46872, 47855, 47856, 47857, 47858, 47859, 47860, 47861, 47862, 47863, 47864, 47865, 47866, 47867, 47868, 47869, 47870, 47871, 47872, 47873, 47874, 47875, 47876, 47877, 47878, 47879, 47880, 47881, 47882, 47883, 47884, 47885, 47886, 47887, 47889, 47891, 47892, 47895, 47897, 47898, 47901, 47903, 47904, 47906, 47907, 47908, 47909, 47910, 47911, 47912, 47913, 47914, 47915, 47916, 47917, 47918, 47919, 47920, 47921, 47922, 47923, 47924, 47925, 47926, 47927, 47928, 47929, 47930, 47931, 47932, 47933, 47934, 47935, 47936, 47938, 48600, 48601, 48602, 48603, 48604, 48605, 48606, 48607, 48608, 48609, 48610, 48611, 48612, 48613, 48614, 48615, 48616, 48617, 48618, 48619, 48620, 48621, 48622, 48623, 48624, 48625, 48626, 48627, 48628, 48629, 48630, 48633, 48634, 48636, 48639, 48640, 48642, 48644, 48645, 48646, 48648, 48650, 48651, 48652, 48653, 48654, 48655, 48656, 48657, 48658, 48659, 48660, 48661, 48662, 48663, 48664, 48665, 48666, 48667, 48668, 48669, 48670, 48671, 48672, 48673, 48674, 48675, 48676, 48677, 49568, 49569, 49570, 49571, 49572, 49573, 49574, 49575, 49576, 49577, 49578, 49579, 49580, 49581, 49582, 49583, 49584, 49585, 49586, 49587, 49588, 49589, 49590, 49591, 49592, 49593, 49594, 49595, 49596, 49597, 49598, 49599, 49600, 49601, 49602, 49603, 49604, 49605, 49606, 49607, 49608, 49609, 49610, 49611, 49612, 49613, 49614, 49615, 49616, 49617, 49618, 49619, 49620, 49621, 49622, 49623, 49624, 49625, 49626, 49627, 49628, 49629, 49630, 49631, 49632, 49633, 49634, 49635, 49636, 49637, 49638, 49639, 49640, 49641, 49642, 49643, 49644, 49645, 49646, 49647, 49648, 49649, 49650, 49651, 49652, 49653, 49654, 49655, 49656, 49657, 49658, 49659, 49660, 49661, 49662, 49663, 49664, 49665, 49666, 49667, 49668, 49669, 49670, 49671, 49672, 49673, 49674, 49675, 49676, 49677, 49678, 49679, 49680, 49681, 49682, 49683, 49684, 49685, 49686, 49687, 49688, 49689, 49690, 49691, 49692, 49693, 49694, 49695, 49696, 49697, 49698, 49699, 49700, 49701, 49702, 49703, 49704, 49705, 49706, 49707, 49708, 51094, 51095, 51096, 51097, 51098, 51099, 51100, 51101, 51102, 51103, 51104, 51105, 51106, 51107, 51108, 51109, 51110, 51111, 51112, 51113, 51114, 51115, 51116, 51117, 51118, 51119, 51120, 51121, 51122, 51123, 51124, 51125, 51127, 51128, 51129, 51130, 51131, 51134, 51135, 51136, 51137, 51140, 51141, 51142, 51143, 51146, 51147, 51148, 51149, 51153, 51154, 51155, 51159, 51160, 51161, 51166, 51167, 51172, 51173, 51178, 51179, 51185, 51191, 51197, 52158, 52159, 52160, 52161, 52162, 52163, 52164, 52165, 52166, 52167, 52168, 52169, 52170, 52171, 52172, 52173, 52174, 52175, 52176, 52177, 52178, 52179, 52180, 52181, 52182, 52183, 52184, 52185, 52186, 52190, 52191, 52192, 52193, 52196, 52197, 52198, 52199, 52202, 52203, 52204, 52205, 52207, 52208, 52209, 52210, 52211, 52212, 52213, 52214, 52215, 52216, 52217, 52218, 52219, 52220, 52221, 52222, 52223, 52224, 52225, 52226, 52227, 52228, 52229, 52230, 52231, 52232, 52234, 52235, 52240, 52984, 52985, 52986, 52987, 52988, 52989, 52990, 52991, 52992, 52993, 52994, 52995, 52996, 52997, 52998, 52999, 53000, 53001, 53002, 53003, 53004, 53005, 53006, 53007, 53008, 53009, 53010, 53011, 53012, 53013, 53014, 53018, 53020, 53024, 53025, 53030, 53031, 53036, 53037, 53041, 53043, 53044, 53045, 53046, 53047, 53048, 53049, 53050, 53051, 53052, 53053, 53054, 53055, 53056, 53057, 53058, 53059, 53060, 53061, 53062, 53063, 53064, 53065, 53066, 53067, 53068, 53069, 53070, 53071, 53072, 53709, 53710, 53711, 53712, 53713, 53714, 53715, 53716, 53717, 53718, 53719, 53720, 53721, 53722, 53723, 53724, 53725, 53726, 53727, 53728, 53729, 53730, 53731, 53732, 53733, 53734, 53735, 53736, 53737, 53738, 53739, 53740, 53741, 53742, 53743, 53744, 53745, 53746, 53747, 53748, 53749, 53750, 53751, 53752, 53753, 53754, 53755, 53756, 53757, 53758, 53759, 53760, 53761, 53762, 53763, 53764, 53765, 53766, 53767, 53768, 53769, 53770, 53771, 53772, 53774, 53775, 53776, 53777, 53778, 53780, 53781, 53782, 53783, 53784, 53785, 53786, 53787, 53788, 53789, 53790, 53791, 53792, 53793, 53794, 53795, 53796, 53797, 53798, 53799, 53800, 53801, 53802, 53803, 53804, 53805, 53806, 53807, 53808, 53809, 53810, 53811, 53812, 53813, 53814, 53815, 53816, 53817, 53818, 53819, 53820, 53821, 53822, 53823, 53824, 53825, 53826, 53827, 53828, 53829, 53830, 53831, 53832, 53833, 53834, 53835, 53836, 53837, 53838, 53839, 53840, 53841, 53842, 53843, 53844, 53845, 53846, 53847, 53848, 53849, 53850, 53851, 53852, 53853, 53854, 53855, 53856, 53857, 53858, 53859, 53860, 53861, 53862, 53863, 53864, 53866, 53869, 53873, 53937, 53943, 53944, 53945, 53946, 53949, 53950, 53951, 53952, 53953, 53954, 53955, 53956, 53957, 53958, 53959, 53960, 53961, 53962, 53963, 53964, 53965, 53966, 53967, 53968, 53969, 53970, 53971, 53972, 53973, 53974, 53975, 53976, 53977, 53978, 53979, 53980, 53981, 53982, 53983, 53984, 53985, 53986, 53987, 53988, 53989, 53990, 53991, 53992, 53993, 53994, 53995, 53996, 53997, 53998, 53999, 54000, 54001, 54002, 54003, 54004, 54005, 54006, 54007, 54008, 54009, 54010, 54011, 54012, 54013, 54014, 54016, 54018, 54019, 54020, 54021, 54022, 54023, 54025, 54026, 54027, 54028, 54029, 54031, 54032, 54034, 54035, 54038, 54040, 54041, 54044, 54046, 54047, 54050, 54052, 54053, 54054, 54056, 54058, 54059, 54060, 54061, 54062, 54063, 54064, 54065, 54066, 54067, 54068, 54069, 54070, 54071, 54072, 54073, 54074, 54075, 54076, 54077, 54078, 54079, 54080, 54081, 54082, 54083, 54084, 54085, 54086, 54087, 54088, 54089, 54090, 54091, 54092, 54093, 54094, 54095, 54096, 54097, 54098, 54099, 56456, 56457, 56458, 56459, 56460, 56461, 56462, 56463, 56464, 56465, 56466, 56467, 56468, 56469, 56470, 56471, 56472, 56473, 56474, 56475, 56476, 56477, 56478, 56479, 56480, 56483, 56484, 56485, 56486, 56489, 56490, 56491, 56492, 56493, 56495, 56496, 56497, 56498, 56499, 56500, 56501, 56502, 56503, 56504, 56505, 56506, 56507, 56508, 56509, 56510, 56511, 56512, 56513, 56514, 56515, 56516, 56517, 56518, 56519, 56520, 56521, 56522, 56523, 56524, 56525, 56526, 56527, 56528, 56531, 56532, 56533, 57248, 57253, 57254, 57260, 57261, 57262, 57266, 57267, 57268, 57272, 57273, 57274, 57278, 57279, 57280, 57284, 57285, 57286, 57289, 57290, 57291, 57292, 57295, 57296, 57301, 57302, 57305, 57307, 57311, 57313, 57319, 57325, 57326, 57327, 57328, 57331, 57332, 57333, 57334, 57336, 57337, 57338, 57339, 57340, 57342, 57343, 57344, 57345, 57346, 57347, 57348, 57349, 57350, 57351, 57352, 57353, 57354, 57355, 57356, 57357, 57358, 57359, 57360, 57361, 57362, 57890, 57891, 57892, 57893, 57894, 57895, 57896, 57897, 57898, 57899, 57900, 57901, 57902, 57903, 57904, 57905, 57906, 57907, 57908, 57909, 57910, 57911, 57912, 57913, 57914, 57915, 57916, 57917, 57918, 57919, 57920, 57921, 57922, 57923, 57924, 57925, 57926, 57927, 57928, 57929, 57930, 57931, 57932, 57933, 57934, 57935, 57936, 57937, 57938, 57939, 57940, 57941, 57942, 57943, 57945, 57946, 57947, 57948, 57949, 57950, 57951, 57952, 57953, 57954, 57955, 57956, 57957, 57958, 57959, 57960, 57961, 57962, 57963, 57964, 57965, 57966, 57967, 57968, 57969, 57970, 57971, 57972, 57973, 57974, 57975, 57976, 57977, 57978, 57979, 57980, 57981, 57982, 57983, 57984, 57985, 57986, 57987, 57988, 57989, 57990, 57991, 57992, 57993, 58193, 58194, 58195, 58196, 58197, 58198, 58199, 58200, 58201, 58202, 58203, 58204, 58205, 58206, 58207, 58208, 58209, 58210, 58211, 58212, 58213, 58214, 58215, 58216, 58217, 58218, 58219, 58220, 58221, 58222, 58223, 58224, 58225, 58226, 58227, 58228, 58229, 58230, 58231, 58233, 58234, 58235, 58236, 58237, 58239, 58240, 58241, 58242, 58243, 58246, 58247, 58248, 58249, 58251, 58252, 58254, 58255, 58256, 58257, 58258, 58259, 58260, 58261, 58262, 58263, 58264, 58265, 58266, 58267, 58268, 58269, 58270, 58271, 58272, 58273, 58274, 58275, 58276, 58277, 58278, 58279, 58280, 58281, 58282, 58283, 60621, 60622, 60623, 60624, 60625, 60626, 60627, 60628, 60629, 60630, 60631, 60632, 60633, 60634, 60635, 60636, 60637, 60638, 60639, 60640, 60641, 60642, 60643, 60644, 60645, 60647, 60649, 60650, 60651, 60652, 60654, 60655, 60656, 60657, 60658, 60660, 60661, 60662, 60663, 60664, 60665, 60666, 60667, 60668, 60669, 60670, 60671, 60672, 60673, 60674, 60675, 60676, 60677, 60678, 60679, 60680, 60681, 60682, 60683, 60684, 60685, 60686, 60687, 60688, 60689, 60690, 60691, 60692, 60693, 60694, 60695, 60697, 60698, 61444, 61450, 61455, 61456, 61461, 61462, 61466, 61467, 61468, 61472, 61477, 61478, 61924, 61925, 61926, 61927, 61928, 61929, 61930, 61931, 61932, 61933, 61934, 61935, 61936, 61937, 61938, 61939, 61940, 61941, 61942, 61943, 61944, 61945, 61946, 61947, 61948, 61949, 61950, 61951, 61952, 61953, 61954, 61955, 61956, 61957, 61958, 61959, 61960, 61961, 61962, 61963, 61964, 61965, 61966, 61967, 61968, 61970, 61971, 61973, 61974, 61976, 61977, 61979, 61980, 61982, 61983, 61985, 61986, 61988, 61989, 61990, 61992, 61993, 61994, 61995, 61996, 61997, 61998, 61999, 62000, 62001, 62002, 62003, 62004, 62005, 62006, 62007, 62008, 62009, 62010, 62011, 62012, 62013, 62014, 62015, 62016, 62017, 62018, 62019, 62020, 62021, 62022, 62023, 62024, 62025, 62026, 62029, 62030, 62032, 62034, 84698, 84699, 84700, 84701, 84702, 84703, 84704, 84705, 84706, 84707, 84708, 84709, 84710, 84711, 84712, 84713, 84714, 84715, 84716, 84717, 84718, 84719, 133190, 133204, 133237, 133240, 133246, 133251, 133283, 133287, 133290, 133294, 133299, 133331, 133335, 133338, 133342, 133347, 133380, 133383, 133387, 133393, 133399, 133433, 133437, 133441, 133447, 133453, 133487, 133491, 133503, 133534, 133538, 133542, 133550, 133581, 133584, 133587, 133597, 133630, 133634, 133637, 133647, 133684, 133697, 133730, 133734, 133781, 133785, 133789, 133793, 133799, 133833, 133837, 133841, 133845, 133884, 133887, 133891, 133895, 133901, 133934, 133937, 133941, 133945, 133951, 134001, 134050, 134300, 134306, 134311, 134352, 134358, 134363, 134395, 134399, 134403, 134408, 134413, 134446, 134450, 134454, 134459, 134464, 134498, 134502, 134507, 134512, 134518, 134552, 134556, 134561, 134566, 134572, 134606, 134611, 134616, 134626, 134660, 134665, 134670, 134680, 134719, 134723, 134733, 134766, 134771, 134776, 134824, 134840, 134874, 134879, 134884, 134889, 134929, 134933, 134938, 134947, 134980, 134984, 134989, 135030, 135034, 135044, 135082, 135086, 135091, 135096, 135102, 135402, 135411, 135416, 135422, 135456, 135461, 135466, 135472, 135478, 135513, 135518, 135523, 135529, 135535, 135569, 135574, 135580, 135586, 135592, 135627, 135632, 135638, 135644, 135650, 135690, 135695, 135707, 135747, 135752, 135764, 135804, 135861, 135879, 135914, 135925, 135971, 135977, 135983, 135989, 135995, 136029, 136034, 136040, 136046, 136052, 136087, 136092, 136098, 136104, 136110, 136149, 136155, 136161, 136167, 136202, 136206, 136212, 136218, 136224, 136561, 136621, 136627, 136662, 136663, 136669, 136675, 136681, 136687, 136722, 136723, 136724, 136729, 136735, 136741, 136747, 136782, 136783, 136784, 136789, 136795, 136801, 136807, 136842, 136843, 136844, 136845, 136846, 136852, 136858, 136869, 136903, 136904, 136905, 136906, 136907, 136913, 136919, 136924, 136930, 136964, 136965, 136966, 136967, 136974, 136980, 137027, 137028, 137029, 137030, 137037, 137043, 137090, 137091, 137092, 137100, 137123, 137151, 137152, 137153, 137161, 137178, 137212, 137213, 137214, 137215, 137217, 137222, 137228, 137234, 137240, 137273, 137274, 137275, 137276, 137277, 137278, 137283, 137289, 137295, 137301, 137334, 137335, 137336, 137337, 137338, 137339, 137344, 137349, 137355, 137361, 137396, 137397, 137398, 137399, 137400, 137401, 137406, 137411, 137417, 137423, 137458, 137459, 137460, 137461, 137468, 137474, 137486, 137521, 137522, 137523, 137524, 137525, 137526, 137531, 137537, 137543, 137549, 137892, 137898, 137910, 137952, 137958, 137964, 137970, 137976, 138012, 138018, 138024, 138030, 138036, 138072, 138078, 138084, 138090, 138131, 138137, 138143, 138149, 138190, 138196, 138208, 138214, 138250, 138256, 138262, 138268, 138274, 138310, 138334, 138370, 138431, 138455, 138492, 138498, 138504, 138510, 138553, 138559, 138565, 138571, 138613, 138619, 138625, 138631, 138673, 138679, 138685, 138691, 138734, 138740, 138746, 138752, 139062, 139123, 139160, 139166, 139172, 139178, 139184, 139221, 139227, 139233, 139239, 139245, 139282, 139288, 139294, 139300, 139343, 139349, 139355, 139361, 139404, 139410, 139416, 139422, 139463, 139469, 139475, 139481, 139523, 139529, 139583, 139589, 139642, 139666, 139703, 139715, 139765, 139777, 139783, 139827, 139833, 139839, 139845, 139889, 139895, 139901, 139907, 139913, 139951, 139957, 139963, 139969, 139975, 140037, 140075, 140081, 140087, 140093, 140099], "numberOfPointsInBox": 2236}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "f4241163-2e06-4bea-ba41-1e1c53a0d205", "objectId": "c8ef60c9-73b9-49cd-a2e8-14588b7c0be8", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [1387, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 2063, 2067, 2069, 2075, 2080, 2081, 4508, 4509, 4510, 4511, 4512, 4513, 4514, 4515, 4516, 4518, 4519, 4521, 4522, 4523, 4524, 4525, 4526, 4527, 4528, 4529, 4530, 5532, 5533, 5534, 5535, 5536, 5537, 5538, 5539, 5540, 5541, 5542, 5543, 5544, 5545, 5546, 7588, 7589, 7590, 7591, 7592, 7593, 7594, 7595, 7596, 7597, 7598, 7599, 7600, 7602, 7603, 7604, 7605, 7606, 7607, 7608, 7609, 7610, 7611, 7612, 8618, 8620, 8621, 8622, 8623, 8624, 8625, 8626, 8628, 8629, 8630, 8631, 8632, 8633, 8634, 8635, 8636, 8637, 8638, 8666, 8667, 8668, 8669, 8670, 8671, 8672, 8673, 8674, 8675, 8676, 8677, 8678, 8680, 8681, 8683, 8684, 8686, 8687, 8688, 8689, 8690, 8691, 8692, 8693, 8694, 8695, 8696, 8697, 8698, 8699, 8702, 10584, 10586, 10587, 10588, 10589, 10591, 10592, 10593, 10594, 10595, 10597, 10599, 10601, 10602, 10603, 10604, 10605, 10606, 10607, 10609, 10610, 10611, 10612, 11600, 11601, 11602, 11603, 11604, 11605, 11606, 11607, 11608, 11609, 11610, 11611, 11612, 11613, 11614, 11615, 11616, 11617, 11618, 11619, 11620, 11621, 11622, 11857, 11858, 11859, 11860, 11861, 11862, 11863, 11864, 11865, 11866, 11867, 11868, 11869, 11871, 11873, 11874, 11875, 11876, 11877, 11878, 11879, 11880, 11881, 11882, 13623, 13624, 13625, 13630, 13631, 13640, 13641, 13642, 13643, 13649, 14638, 14639, 14640, 14641, 14642, 14643, 14644, 14645, 14646, 14647, 14648, 14649, 14650, 14651, 14652, 14653, 14654, 14655, 14656, 14657, 14658, 14659, 14660, 15093, 15094, 15095, 15096, 15097, 15098, 15099, 15100, 15101, 15102, 15105, 15107, 15108, 15109, 15111, 15112, 15113, 15114, 15115, 15117, 48124, 48126, 48127, 48128, 48129, 48130, 48131, 48132, 48133, 48134, 48135, 48136, 48137, 48138, 48139, 48140, 48141, 48142, 48143, 48144, 48145, 48146, 48147, 48148, 48150, 48151, 48152, 48153, 48155, 48156, 48157, 48158, 48160, 48161, 48162, 48163, 48164, 48165, 48166, 48167, 48168, 48169, 48170, 48171, 48172, 48173, 48174, 48175, 48176, 48177, 48178, 48179, 48180, 48181, 48182, 48183, 48184, 48185, 48186, 48187, 48188, 48189, 48190, 48191, 48192, 48193, 48194, 48195, 48196, 48197, 48198, 48199, 48200, 48201, 48202, 48203, 48204, 48205, 48206, 48207, 48208, 48209, 48210, 48211, 48212, 48213, 48214, 48215, 48216, 48217, 48218, 48219, 48220, 48221, 48222, 48223, 48224, 48225, 48226, 48227, 48228, 48229, 48230, 48231, 48232, 48233, 48234, 48235, 48236, 48237, 48238, 48239, 48240, 48241, 48242, 48243, 48244, 48245, 48246, 48247, 48248, 48249, 48250, 48251, 48252, 48253, 48254, 48255, 48256, 48257, 48258, 48259, 48260, 48261, 48262, 48263, 48264, 48265, 48266, 48267, 48268, 48269, 48270, 48271, 48272, 48274, 48275, 48276, 48277, 48278, 48280, 48282, 48283, 48284, 48285, 48286, 48288, 48291, 48294, 48297, 48299, 48300, 48302, 48303, 48304, 48305, 48306, 48307, 48308, 48309, 48310, 48311, 48312, 48313, 48314, 48315, 48316, 48317, 48318, 48319, 48320, 48321, 48322, 48323, 48324, 48325, 48326, 48327, 48328, 52488, 52489, 52490, 52491, 52492, 52493, 52494, 52495, 52496, 52497, 52498, 52499, 52500, 52501, 52502, 52503, 52504, 52505, 52506, 52507, 52508, 52509, 52510, 52511, 52513, 52516, 52519, 52522, 52524, 52525, 52528, 52530, 52531, 52532, 52533, 52534, 52536, 52537, 52538, 52539, 52540, 52541, 52542, 52543, 52544, 52545, 52546, 52547, 52548, 52549, 52550, 52551, 52552, 52553, 52554, 52555, 52556, 52557, 52558, 52559, 52560, 52561, 52562, 52563, 52564, 52565, 52566, 52567, 52568, 52569, 52570, 52571, 52572, 52573, 52575, 52576, 52577, 52578, 52582, 52583, 52584, 52588, 52589, 52594, 52595, 52600, 52601, 52606, 52607, 52613, 52614, 52619, 52620, 52621, 52622, 52625, 52626, 52627, 52628, 52631, 52632, 52633, 52634, 52638, 52639, 52640, 52644, 52645, 52646, 52650, 52651, 52652, 52656, 52657, 52658, 52664, 52669, 52675, 52679, 52686, 52688, 52692, 52694, 52700, 52706, 52712, 56830, 56836, 56842, 56848, 56854, 131232, 131233, 131237, 131274, 131275, 131277, 131317, 131325, 131361, 131362, 131365, 131448, 132119, 132158, 132161, 132200, 132245, 132290, 132291, 132298, 132336, 132338, 132341, 133103, 133105, 133106, 133145, 133148, 133149, 133188, 133197, 133235, 133238, 133241, 133333, 134082, 134083, 134093, 134132, 134133, 134181, 134182, 134183, 134187, 134236, 134237, 134238, 134242, 134246, 134251, 134291, 134292, 134293, 134304, 134343, 134344, 134345, 134350, 134354, 135241, 135295, 135300, 135351, 135405, 135409, 135516, 135521, 136373, 136384, 136432, 136438, 136443, 136559, 136607, 136667, 136673], "numberOfPointsInBox": 659}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "af8bc1d1-fd49-4c06-9dca-f2ba7ebc7e0c", "objectId": "5029d606-305c-4276-9cb9-5ae2ef61d285", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [1364, 1365, 1366, 1367, 1369, 1371, 1372, 1373, 1374, 1375, 1376, 1377, 1378, 2117, 2128, 2129, 4468, 4469, 4470, 4471, 4472, 4473, 4474, 4475, 4476, 4477, 4478, 4479, 4480, 4481, 5572, 5573, 5574, 5575, 5577, 5578, 5579, 5580, 5581, 5583, 5584, 5586, 5587, 5588, 5590, 5591, 5592, 5593, 5597, 5598, 7545, 7546, 7547, 7548, 7549, 7550, 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558, 8734, 8735, 8736, 8737, 8738, 8739, 8740, 8741, 8742, 8743, 8744, 8745, 8746, 8747, 8748, 10537, 10541, 10542, 10544, 10545, 10546, 10547, 10548, 10550, 10551, 10552, 10553, 10554, 11919, 11920, 11921, 11922, 11923, 11924, 11925, 11926, 11927, 11928, 11929, 11930, 11931, 11932, 11934, 13570, 13571, 13572, 13576, 13577, 13578, 13582, 13583, 13584, 14616, 14617, 14618, 14619, 14620, 14621, 14622, 14624, 14625, 14626, 15149, 15150, 15153, 15154, 15155, 15156, 15159, 15160, 15161, 15164, 16723, 16729, 130815, 130856, 130859, 130897, 130937, 130940, 131756, 131758, 131799, 131802, 131805, 131844, 131846, 131890, 131892, 132649, 132652, 132696, 132699, 132742, 132787, 132790, 133681, 133731, 133735, 133782, 133834, 133838, 134716, 134768, 134773, 134821, 134826, 134876, 134881, 135864, 135917, 135974, 135980], "numberOfPointsInBox": 177}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "1d158fb8-2d57-4a3d-94b5-07acccd5aefd", "objectId": "e32d3223-1292-4c78-b0cd-46ce6ad9a0bc", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [316, 323, 329, 335, 1346, 1347, 1348, 1349, 1352, 1354, 1357, 2141, 2146, 2147, 2152, 2387, 2389, 2391, 2393, 2394, 2396, 2397, 2401, 2404, 2405, 4450, 4451, 4452, 4453, 4454, 4455, 4456, 4457, 4458, 4459, 4460, 4461, 5333, 5606, 5607, 5608, 5609, 5610, 5613, 5614, 5615, 5616, 5617, 5619, 5620, 5622, 7525, 7526, 7527, 7528, 7529, 7531, 7532, 7533, 7534, 7535, 8758, 8759, 8760, 8761, 8762, 8763, 8764, 8765, 8766, 8767, 8768, 8769, 8770, 10520, 10521, 10522, 10523, 10524, 10526, 10527, 10528, 10529, 10530, 11942, 11943, 11944, 11945, 11946, 11947, 11948, 11949, 11950, 11951, 11952, 11953, 13552, 13553, 13554, 13557, 13558, 13559, 14605, 14606, 14607, 14610, 15172, 15173, 15174, 15177, 15179, 15180, 15183, 16698, 16699, 16705, 16710, 130635, 130680, 130723, 130726, 130769, 130772, 131582, 131625, 132476, 132521, 132568, 132607, 132610, 133488, 133492, 133535, 133539, 134504, 134510, 134558, 134608, 134613, 134662, 134667, 135583, 135693, 135698, 135750, 135755], "numberOfPointsInBox": 146}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "36773490-19a5-4894-be95-ffd3ae1eba3f", "objectId": "c305af21-7b22-402f-8cda-3d7b76e9d073", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [305, 311, 317, 1338, 1339, 1341, 1342, 1343, 1344, 2153, 2158, 2159, 2164, 2414, 2418, 2421, 2422, 2426, 2435, 4441, 4442, 4446, 4447, 4448, 5623, 5624, 5625, 5626, 5629, 5630, 5631, 5632, 5633, 5635, 7514, 7516, 7517, 7518, 7519, 7520, 7521, 7522, 7523, 7524, 8771, 8773, 8775, 8776, 8777, 8778, 8779, 8782, 10510, 10512, 10514, 10515, 10516, 10517, 10518, 11954, 11955, 11956, 11958, 11959, 11960, 11961, 11964, 13540, 13541, 13542, 13543, 13546, 13547, 13548, 15184, 15185, 15188, 15189, 15190, 15191, 16686, 16687, 16692, 16693, 130541, 130589, 131407, 131451, 131493, 131496, 131538, 131541, 132385, 132388, 132431, 132434, 133384, 133434, 133438, 135577, 135635], "numberOfPointsInBox": 101}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "a7c91545-2bb1-4342-94b3-cb2e638f0d5a", "objectId": "0650ff28-41a6-4a5f-ac17-89a769d5ffad", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [289, 295, 1325, 2177, 5643, 5644, 5649, 5650, 7504, 7505, 7506, 8788, 10499, 10500, 10501, 10502, 10505, 11969, 11970, 11971, 13532, 13533, 15198, 15200, 15201, 16668, 16669, 16670, 16674], "numberOfPointsInBox": 29}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "0bfdf09d-7f99-4dae-a51a-edfea7a2a1e3", "objectId": "e03bf406-e5fb-4b6b-8bfb-4388de4b1cf1", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [2181, 5362, 7499, 7500, 8793, 15206, 16663, 16664], "numberOfPointsInBox": 8}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "e0d5d414-358a-4c9c-82a1-dbd489a9cc8c", "objectId": "27c9e593-ff11-4b1c-9253-aff4706ddb5b", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [283, 284, 2182, 2185, 5661, 7498, 8798, 10494, 10495, 11979, 15209, 16659, 16660], "numberOfPointsInBox": 13}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "47135a62-ec1d-434d-aa3a-f90d41217e35", "objectId": "0da518da-8c14-4364-b494-646809641015", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [274, 1312, 2188, 4417, 5373, 7489, 7490, 10490, 11981, 15211], "numberOfPointsInBox": 10}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "6f900dfc-f616-484e-a084-8ff2ec190eeb", "objectId": "2165789a-d0d5-42df-93b7-192189b558a2", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [271, 13518, 16654], "numberOfPointsInBox": 3}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "24bbde15-c191-4123-99d4-6886942104fc", "objectId": "e9b747e7-e01f-4209-b082-6986ce17ce2d", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [], "numberOfPointsInBox": 0}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "daf6c303-3afa-4e3d-869d-f3e6ad8309cf", "objectId": "bb1f0700-9c85-4560-a998-fb3f04eeb296", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [], "numberOfPointsInBox": 0}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "817ed6c3-8552-42dd-b858-00bfa37ab8fa", "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", "className": "3D_signal_pole", "geometry": {"associatedPoints": [18760, 18761, 18762, 18763, 18764, 18765, 18766, 18767, 18768, 18769, 18770, 18771, 18772, 18773, 18774, 18775, 18776, 18777, 18778, 18779, 19471, 19477, 19483, 19489, 19494, 19495, 19499, 19500, 19501, 19505, 19506, 19507, 19511, 19512, 19513, 19517, 19518, 19519, 19523, 19524, 19525, 19529, 19530, 19531, 19536, 19537, 20713, 20714, 20715, 20716, 20717, 20718, 20719, 20720, 20721, 20722, 20723, 20724, 20725, 20726, 20727, 20728, 20729, 20730, 20731, 20732, 20733, 20734, 23181, 23182, 23183, 23184, 23185, 23186, 23187, 23188, 23189, 23190, 23191, 23192, 23193, 23194, 23195, 23196, 23197, 23198, 23199, 23200, 23202, 23203, 24429, 24446, 24452, 24458, 24463, 24464, 24469, 24470, 24475, 24476, 24481, 24482, 24487, 24493, 24499, 24505, 24511, 24517, 25141, 25143, 25144, 25145, 25146, 25147, 25148, 25149, 25150, 25151, 25152, 25153, 25154, 25155, 25156, 25157, 25158, 25159, 25160, 25161, 25162, 27580, 27586, 27590, 27591, 27592, 27593, 27594, 27595, 27596, 27597, 27598, 27599, 27600, 27601, 27602, 27603, 27604, 27605, 27606, 27607, 27608, 27609, 27610, 27611, 27612, 27613, 27614, 27615, 27616, 27617, 27622, 27623, 28777, 28778, 28779, 28780, 28781, 28782, 28783, 28784, 28785, 28786, 28787, 28788, 28789, 28790, 28791, 28792, 28793, 28794, 28795, 28796, 28797, 28798, 28799, 28800, 28804, 28805, 28806, 28809, 28810, 28811, 28815, 29533, 29535, 29536, 29537, 29538, 29539, 29540, 29541, 29542, 29543, 29544, 29545, 29546, 29547, 29548, 29549, 29550, 29551, 29552, 29553, 31852, 31856, 31857, 31858, 31862, 31863, 31864, 31865, 31868, 31869, 31870, 31871, 31874, 31875, 31876, 31877, 31880, 31881, 31882, 31883, 31884, 31886, 31887, 31888, 31889, 31890, 31892, 31893, 31894, 31895, 31896, 31898, 31899, 31900, 31901, 31902, 31903, 31904, 31905, 31906, 31907, 31908, 31909, 31910, 31911, 31912, 31913, 31914, 31915, 31916, 31917, 31918, 31919, 31920, 31921, 31923, 31924, 31925, 31926, 31927, 31929, 31930, 31931, 31932, 31933, 31936, 31937, 31938, 31939, 31943, 31944, 31945, 31949, 31950, 31951, 31956, 31957, 31962, 31963, 31968, 31969, 31975, 31981, 31987, 32995, 32997, 32998, 32999, 33000, 33001, 33002, 33003, 33004, 33005, 33006, 33007, 33008, 33009, 33010, 33011, 33012, 33013, 33014, 33015, 33016, 33018, 33019, 33024, 33025, 33782, 33784, 33785, 33786, 33787, 33788, 33789, 33790, 33791, 33792, 33793, 33794, 33795, 33796, 33797, 33798, 33799, 33800, 33801, 33802, 33803, 33804, 33806, 33812, 33818, 33824, 37166, 37167, 37168, 37169, 37170, 37171, 37172, 37173, 37174, 37175, 37176, 37177, 37178, 37179, 37180, 37181, 37182, 37183, 37184, 37937, 37943, 37944, 37945, 37946, 37947, 37949, 37950, 37951, 37952, 37953, 37954, 37955, 37956, 37957, 37958, 37959, 37960, 37961, 37962, 37963, 37964, 37965, 37966, 37967, 37968, 37969, 37970, 37971, 37972, 37973, 37974, 37975, 37976, 37979, 37980, 37981, 37982, 37985, 37986, 37987, 37988, 37991, 37992, 37993, 37997, 37998, 37999, 38915, 38921, 38927, 38934, 38940, 38946, 38959, 38965, 38971, 38978, 38984, 38987, 38990, 38993, 38999, 39000, 39005, 39006, 39009, 39011, 39012, 39017, 39018, 39019, 39024, 39025, 39028, 39029, 39030, 39031, 39032, 39033, 39035, 39036, 39037, 39038, 39039, 39040, 39041, 39042, 39043, 39044, 39045, 39046, 39047, 39048, 39049, 39050, 39051, 39052, 39053, 39054, 39055, 39056, 39057, 39058, 39059, 39060, 39061, 39062, 39063, 39064, 39065, 39066, 39067, 39068, 39069, 39070, 39071, 39072, 39073, 39074, 39075, 39076, 39077, 39078, 39079, 91770, 91771, 91772, 91773, 91774, 122356, 122402, 122420, 122460, 122466, 122472, 122478, 122484, 122524, 122529, 122535, 122541, 122547, 122587, 122592, 122598, 122604, 122610, 122670, 122730, 122794, 122852, 122858, 122921, 122984, 123719, 123725, 123737, 123776, 123782, 123788, 123794, 123799, 123837, 123843, 123849, 123855, 123860, 123898, 123904, 123915, 123921, 123960, 123966, 123977, 123983, 124044, 124105, 124166, 124227, 124972, 125012, 125035, 125075, 125081, 125087, 125092, 125098, 125138, 125143, 125149, 125155, 125161, 125167, 125201, 125206, 125212, 125218, 125224, 125230, 125282, 125288, 125340, 125346, 125402, 125407, 125463, 125468, 125526, 125589, 126256, 126306, 126311, 126316, 126354, 126355, 126371, 126376, 126382, 126415, 126416, 126421, 126426, 126432, 126437, 126443, 126476, 126477, 126481, 126485, 126496, 126502, 126535, 126536, 126540, 126544, 126555, 126561, 126614, 126620, 126673, 126679, 126733, 126793, 127388, 127444, 127489, 127494, 127500, 127549, 127554, 127587, 127592, 127596, 127601, 127606, 127640, 127644, 127649, 127653, 127659, 127694, 127698, 127703, 127707, 127713, 127758, 127764, 127797, 127801, 127809, 127815, 127863, 127917, 127922, 127972, 128027, 128549, 128591, 128595, 128600, 128642, 128646, 128651, 128687, 128690, 128694, 128699, 128703, 128739, 128742, 128746, 128751, 128755, 128791, 128803, 128809, 128845, 128849, 128857, 128863, 128912, 128918, 128967, 128973, 129021, 129074, 129079], "numberOfPointsInBox": 674}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "6cdb49ee-118b-46c4-a17b-ab40bc4544a6", "objectId": "418cad49-3c39-4fce-ae3e-c4b6db04499d", "className": "3D_catenary_pole", "geometry": {"associatedPoints": [84422, 92241, 92242, 92243, 92244, 92245, 92246, 92247, 92248, 92249, 92250, 92251, 92252, 92253, 92254, 92255, 92256, 92257, 92258, 92259], "numberOfPointsInBox": 20}, "attributes": {"structure": "structured"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "059f694f-3215-41ba-94b0-74a9cd42b898", "objectId": "af9f7ffb-9c2a-481c-beab-843cfa428cda", "className": "3D_signal_pole", "geometry": {"associatedPoints": [246, 247, 2526, 4368, 5695, 5696, 7459, 7462, 8824, 10469, 12002, 15240, 15241], "numberOfPointsInBox": 13}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "9955441a-e315-49f7-85b4-cd8f1b3d0ba0", "objectId": "369bd0f5-4f00-4fbf-8f12-23c8839ce0e0", "className": "3D_signal_pole", "geometry": {"associatedPoints": [], "numberOfPointsInBox": 0}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "e7cbd2d8-16e4-46e0-9717-3c72d3c07996", "objectId": "0fdb210a-34dd-4cee-8684-fe1756bbe023", "className": "3D_signal_pole", "geometry": {"associatedPoints": [], "numberOfPointsInBox": 0}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "72869011-d5c7-4857-8fc9-d4eb8ecba975", "objectId": "decf17bb-d644-4eb8-821e-c578738b3082", "className": "3D_train", "geometry": {"associatedPoints": [5718, 5723, 8834, 10448, 10452, 12031, 12034, 12037, 13470, 13473, 16597, 35457, 35459], "numberOfPointsInBox": 13}, "attributes": {"type": "regionalExpress"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "7bf23c73-a99b-4389-ac43-d3496cc34fff", "objectId": "f24c55f7-2ff0-4137-9328-5ae022bfcb07", "className": "3D_train", "geometry": {"associatedPoints": [], "numberOfPointsInBox": 0}, "attributes": {"type": "regionalExpress"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "875b494e-dbf6-4bb2-b5e6-d70b65fb5dbe", "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", "className": "3D_person", "geometry": {"associatedPoints": [269, 2194, 2498, 3408, 4402, 5377, 5680, 6528, 6530, 7481, 8485, 8809, 11480, 15217, 15731, 16651], "numberOfPointsInBox": 16}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "age": "adult", "function": "worker", "pose": "upright"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "ee6e9de9-8854-4467-9f5c-a95073ae24ad", "objectId": "87563e91-2b69-475b-9a8b-07f794d1bdb2", "className": "3D_person", "geometry": {"associatedPoints": [1282, 2200, 3402, 5387, 7477, 8813, 15221, 16648], "numberOfPointsInBox": 8}, "attributes": {"isDummy": false, "carries": "nothing", "connectedTo": [], "isDistracted": false, "function": "worker", "age": "adult", "pose": "upright"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "b44b68c1-4e80-46f1-b822-00f453b1c227", "objectId": "e07310f7-02df-4d05-ac3c-6296117d27ea", "className": "3D_track", "geometry": {"associatedPoints": [618, 624, 625, 630, 631, 636, 637, 638, 642, 643, 644, 648, 649, 650, 651, 654, 655, 656, 657, 660, 661, 662, 663, 664, 666, 667, 668, 669, 670, 672, 673, 674, 675, 676, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1116, 1117, 1118, 1119, 1120, 1122, 1123, 1124, 1125, 1126, 1128, 1129, 1130, 1131, 1134, 1135, 1136, 1137, 1140, 1141, 1142, 1143, 1146, 1147, 1148, 1152, 1153, 1154, 1158, 1159, 1160, 1164, 1165, 1170, 1171, 1176, 1177, 1182, 1183, 1188, 1194, 1200, 1206, 1212, 2191, 3409, 3410, 3411, 3413, 3419, 3422, 3428, 3433, 3439, 3445, 3451, 3457, 3463, 3469, 3475, 3481, 3487, 3493, 3499, 3505, 3511, 3517, 3523, 3529, 3535, 3536, 3541, 3542, 3547, 3548, 3553, 3554, 3559, 3560, 3565, 3566, 3567, 3571, 3572, 3573, 3577, 3578, 3579, 3583, 3584, 3585, 3586, 3589, 3590, 3591, 3592, 3595, 3596, 3597, 3598, 3601, 3602, 3603, 3604, 3605, 3607, 3608, 3609, 3610, 3611, 3613, 3614, 3615, 3616, 3617, 3619, 3620, 3621, 3622, 3623, 3624, 3625, 3626, 3627, 3628, 3629, 3630, 3631, 3632, 3633, 3634, 3635, 3636, 3637, 3638, 3639, 3640, 3641, 3642, 3643, 3644, 3645, 3646, 3647, 3648, 3649, 3650, 3651, 3652, 3653, 3654, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662, 3663, 3664, 3665, 3666, 3667, 3668, 3669, 3670, 3671, 3672, 3673, 3674, 3675, 3676, 3677, 3678, 3679, 3680, 3681, 3682, 3683, 3684, 3685, 3686, 3687, 3688, 3689, 3690, 3691, 3692, 3693, 3694, 3695, 3696, 3697, 3698, 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3706, 3707, 3708, 3709, 3710, 3711, 3712, 3713, 3714, 3715, 3716, 3717, 3718, 3719, 3720, 3721, 3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, 3732, 3733, 3734, 3735, 3736, 3737, 3738, 3739, 3740, 3741, 3742, 3743, 3744, 3745, 3746, 3747, 3748, 3749, 3750, 3751, 3752, 3753, 3754, 3755, 3756, 3757, 3758, 3759, 3760, 3761, 3762, 3763, 3764, 3765, 3766, 3767, 3768, 3769, 3770, 3771, 3772, 3773, 3774, 3775, 3776, 3777, 3778, 3779, 3780, 3781, 3782, 3783, 3784, 3785, 3786, 3787, 3788, 3789, 3790, 3791, 3792, 3793, 3794, 3795, 3796, 3797, 3798, 3799, 3800, 3801, 3802, 3803, 3804, 3805, 3806, 3807, 3808, 3809, 3810, 3811, 3812, 3813, 3814, 3815, 3816, 3817, 3818, 3819, 3820, 3821, 3822, 3823, 3824, 3825, 3826, 3827, 3828, 3829, 3830, 3831, 3832, 3833, 3834, 3835, 3836, 3837, 3838, 3839, 3840, 3841, 3842, 3843, 3844, 3845, 3846, 3847, 3848, 3849, 3850, 3851, 3852, 3853, 3854, 3855, 3856, 3857, 3858, 3859, 3860, 3861, 3862, 3863, 3864, 3865, 3866, 3867, 3868, 3869, 3870, 3871, 3872, 3873, 3874, 3875, 3876, 3877, 3878, 3879, 3880, 3881, 3882, 3883, 3884, 3885, 3886, 3887, 3888, 3889, 3890, 3891, 3892, 3893, 3894, 3895, 3896, 3897, 3898, 3899, 3900, 3901, 3902, 3903, 3904, 3905, 3906, 3907, 3908, 3909, 3910, 3911, 3912, 3913, 3914, 3915, 3916, 3917, 3918, 3919, 3920, 3921, 3922, 3923, 3924, 3925, 3926, 3927, 3928, 3929, 3930, 3931, 3932, 3933, 3934, 3935, 3936, 3937, 3938, 3939, 3940, 3941, 3942, 3943, 3944, 3945, 3946, 3947, 3948, 3949, 3950, 3951, 3952, 3953, 3954, 3955, 3956, 3957, 3958, 3959, 3960, 3961, 3962, 3963, 3964, 3965, 3966, 3967, 3968, 3969, 3970, 3971, 3972, 3973, 3974, 3975, 3976, 3977, 3978, 3979, 3980, 3981, 3982, 3983, 3984, 3985, 3986, 3987, 3988, 3989, 3990, 3991, 3992, 3993, 3994, 3995, 3996, 3997, 3998, 3999, 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050, 4051, 4052, 4053, 4054, 4055, 4056, 4057, 4058, 4059, 4060, 4061, 4062, 4063, 4064, 4065, 4066, 4067, 4068, 4069, 4070, 4071, 4072, 4073, 4074, 4075, 4076, 4077, 4078, 4079, 4080, 4081, 4082, 4083, 4084, 4085, 4087, 4088, 4089, 4090, 4091, 4093, 4094, 4095, 4096, 4099, 4100, 4101, 4102, 4105, 4106, 4107, 4108, 4111, 4112, 4113, 4117, 4118, 4119, 4123, 4124, 4129, 4130, 4135, 4136, 4141, 4147, 5368, 5375, 5376, 6531, 6532, 6533, 6534, 6535, 6536, 6537, 6538, 6539, 6540, 6541, 6542, 6544, 6545, 6546, 6548, 6549, 6550, 6551, 6552, 6553, 6555, 6556, 6557, 6558, 6559, 6560, 6561, 6562, 6563, 6565, 6566, 6567, 6568, 6569, 6570, 6571, 6572, 6573, 6574, 6575, 6576, 6577, 6578, 6579, 6580, 6581, 6582, 6583, 6584, 6585, 6586, 6587, 6588, 6589, 6590, 6591, 6592, 6593, 6594, 6595, 6596, 6597, 6598, 6599, 6600, 6601, 6602, 6603, 6604, 6605, 6606, 6607, 6608, 6609, 6610, 6611, 6612, 6613, 6614, 6615, 6616, 6617, 6618, 6619, 6620, 6621, 6622, 6623, 6624, 6625, 6626, 6627, 6628, 6629, 6630, 6631, 6632, 6633, 6634, 6635, 6636, 6637, 6638, 6639, 6640, 6641, 6642, 6643, 6644, 6645, 6646, 6647, 6648, 6649, 6650, 6651, 6652, 6653, 6654, 6655, 6656, 6657, 6658, 6659, 6660, 6661, 6662, 6663, 6664, 6665, 6666, 6667, 6668, 6669, 6670, 6671, 6672, 6673, 6674, 6675, 6676, 6677, 6678, 6679, 6680, 6681, 6682, 6683, 6684, 6685, 6686, 6687, 6688, 6689, 6690, 6691, 6692, 6693, 6694, 6695, 6696, 6697, 6698, 6699, 6700, 6701, 6702, 6703, 6704, 6705, 6706, 6707, 6708, 6709, 6710, 6711, 6712, 6713, 6714, 6715, 6716, 6717, 6718, 6719, 6720, 6721, 6722, 6723, 6724, 6725, 6726, 6727, 6728, 6729, 6730, 6731, 6732, 6733, 6734, 6735, 6736, 6737, 6738, 6739, 6740, 6741, 6742, 6743, 6744, 6745, 6746, 6747, 6748, 6749, 6750, 6751, 6752, 6753, 6754, 6755, 6756, 6757, 6758, 6759, 6760, 6761, 6762, 6763, 6764, 6765, 6766, 6767, 6768, 6769, 6770, 6771, 6772, 6773, 6774, 6775, 6776, 6777, 6778, 6779, 6780, 6781, 6782, 6783, 6784, 6785, 6786, 6787, 6788, 6789, 6790, 6791, 6792, 6793, 6794, 6795, 6796, 6797, 6798, 6799, 6800, 6801, 6802, 6803, 6804, 6805, 6806, 6807, 6808, 6809, 6810, 6811, 6812, 6813, 6814, 6815, 6816, 6817, 6818, 6819, 6820, 6821, 6822, 6823, 6824, 6825, 6826, 6827, 6828, 6829, 6830, 6831, 6832, 6833, 6834, 6835, 6836, 6837, 6838, 6839, 6840, 6841, 6842, 6843, 6844, 6845, 6846, 6847, 6848, 6849, 6850, 6851, 6852, 6853, 6854, 6855, 6856, 6857, 6858, 6859, 6860, 6861, 6862, 6863, 6864, 6865, 6866, 6867, 6868, 6869, 6870, 6871, 6872, 6873, 6874, 6875, 6876, 6877, 6878, 6879, 6880, 6881, 6882, 6883, 6884, 6885, 6886, 6887, 6888, 6889, 6890, 6891, 6892, 6893, 6894, 6895, 6896, 6897, 6898, 6899, 6900, 6901, 6902, 6903, 6904, 6905, 6906, 6907, 6908, 6909, 6910, 6911, 6912, 6913, 6914, 6915, 6916, 6917, 6918, 6919, 6920, 6921, 6922, 6923, 6924, 6925, 6926, 6927, 6928, 6929, 6930, 6931, 6932, 6933, 6934, 6935, 6936, 6937, 6938, 6939, 6940, 6941, 6942, 6943, 6944, 6945, 6946, 6947, 6948, 6949, 6950, 6951, 6952, 6953, 6954, 6955, 6956, 6957, 6958, 6959, 6960, 6961, 6962, 6963, 6964, 6965, 6966, 6967, 6968, 6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978, 6979, 6980, 6981, 6982, 6983, 6984, 6985, 6986, 6987, 6988, 6989, 6990, 6991, 6992, 6993, 6994, 6995, 6996, 6997, 6998, 6999, 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7008, 7009, 7010, 7011, 7012, 7014, 7015, 7016, 7017, 7018, 7020, 7021, 7022, 7023, 7026, 7027, 7028, 7029, 7032, 7033, 7034, 7038, 7039, 7040, 7044, 7045, 7050, 7051, 7056, 7062, 8415, 8421, 8427, 8433, 8434, 8439, 8440, 8445, 8446, 8447, 8451, 8452, 8453, 8454, 8455, 8459, 8460, 8463, 8464, 8465, 8468, 8469, 8470, 8471, 8472, 8474, 8475, 8477, 8478, 8479, 8480, 8481, 8483, 8484, 9584, 9585, 9586, 9587, 9588, 9589, 9590, 9591, 9592, 9593, 9594, 9595, 9596, 9597, 9598, 9599, 9600, 9601, 9602, 9603, 9604, 9605, 9606, 9607, 9608, 9609, 9610, 9611, 9612, 9613, 9614, 9615, 9616, 9617, 9618, 9619, 9620, 9621, 9622, 9623, 9624, 9625, 9626, 9627, 9628, 9629, 9630, 9631, 9632, 9633, 9634, 9635, 9636, 9637, 9638, 9639, 9640, 9641, 9642, 9643, 9644, 9645, 9646, 9647, 9648, 9649, 9650, 9651, 9652, 9653, 9654, 9655, 9656, 9657, 9658, 9659, 9660, 9661, 9662, 9663, 9664, 9665, 9666, 9667, 9668, 9669, 9670, 9671, 9672, 9673, 9674, 9675, 9676, 9677, 9678, 9679, 9680, 9681, 9682, 9683, 9684, 9685, 9686, 9687, 9688, 9689, 9690, 9691, 9692, 9693, 9694, 9695, 9696, 9697, 9698, 9699, 9700, 9701, 9702, 9703, 9704, 9705, 9706, 9707, 9708, 9709, 9710, 9711, 9712, 9713, 9714, 9715, 9716, 9717, 9718, 9719, 9720, 9721, 9722, 9723, 9724, 9725, 9726, 9727, 9728, 9729, 9730, 9731, 9732, 9733, 9734, 9735, 9736, 9737, 9738, 9739, 9740, 9741, 9742, 9743, 9744, 9745, 9746, 9747, 9748, 9749, 9750, 9751, 9752, 9753, 9754, 9755, 9756, 9757, 9758, 9759, 9760, 9761, 9762, 9763, 9764, 9765, 9766, 9767, 9768, 9769, 9770, 9771, 9772, 9773, 9774, 9775, 9776, 9777, 9778, 9779, 9780, 9781, 9782, 9783, 9784, 9785, 9786, 9787, 9788, 9789, 9790, 9791, 9792, 9793, 9794, 9795, 9796, 9797, 9798, 9799, 9800, 9801, 9802, 9803, 9804, 9805, 9806, 9807, 9808, 9809, 9810, 9811, 9812, 9813, 9814, 9815, 9816, 9817, 9818, 9819, 9820, 9821, 9822, 9823, 9824, 9825, 9826, 9827, 9828, 9829, 9830, 9831, 9832, 9833, 9834, 9835, 9836, 9837, 9838, 9839, 9840, 9841, 9842, 9843, 9844, 9845, 9846, 9847, 9848, 9849, 9850, 9851, 9852, 9853, 9854, 9855, 9856, 9857, 9858, 9860, 9861, 9862, 9863, 9864, 9866, 9867, 9868, 9869, 9870, 9872, 9873, 9874, 9875, 9878, 9879, 9880, 9881, 9884, 9885, 9886, 9887, 9890, 9891, 9892, 9896, 9897, 9898, 9902, 9903, 9908, 9909, 9914, 9915, 9920, 9926, 11278, 11284, 11290, 11291, 11296, 11297, 11302, 11303, 11308, 11309, 11310, 11314, 11315, 11316, 11320, 11321, 11322, 11323, 11326, 11327, 11328, 11329, 11332, 11333, 11334, 11335, 11338, 11339, 11340, 11341, 11342, 11344, 11345, 11346, 11347, 11348, 11350, 11351, 11352, 11353, 11354, 11356, 11357, 11358, 11359, 11360, 11362, 11363, 11364, 11365, 11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 11374, 11375, 11376, 11377, 11378, 11379, 11380, 11381, 11382, 11383, 11384, 11385, 11386, 11387, 11388, 11389, 11390, 11391, 11392, 11393, 11394, 11395, 11396, 11397, 11398, 11399, 11400, 11401, 11402, 11403, 11404, 11405, 11406, 11407, 11408, 11409, 11410, 11411, 11412, 11413, 11414, 11415, 11416, 11417, 11418, 11419, 11420, 11421, 11422, 11423, 11424, 11425, 11426, 11427, 11428, 11429, 11430, 11431, 11432, 11433, 11434, 11435, 11436, 11437, 11438, 11439, 11440, 11441, 11442, 11443, 11444, 11445, 11446, 11447, 11448, 11449, 11450, 11451, 11452, 11453, 11454, 11455, 11456, 11457, 11458, 11459, 11460, 11461, 11462, 11463, 11464, 11465, 11466, 11467, 11468, 11469, 11470, 11471, 11472, 11473, 11474, 11475, 11477, 11478, 11479, 11481, 11482, 11483, 11484, 11485, 11486, 11487, 12649, 12650, 12651, 12652, 12653, 12654, 12655, 12656, 12657, 12658, 12659, 12660, 12663, 12664, 12665, 12667, 12668, 12669, 12671, 12672, 12673, 12676, 12677, 12678, 12679, 12680, 12681, 12685, 12686, 12687, 12688, 12690, 12691, 12692, 12693, 12696, 12697, 12698, 12699, 12702, 12703, 12704, 12705, 12708, 12709, 12710, 12711, 12714, 12715, 12716, 12720, 12721, 12722, 12726, 12727, 12728, 12732, 12733, 12734, 12738, 12739, 12740, 12744, 12745, 12746, 12750, 12751, 12756, 12757, 12762, 12763, 12768, 12769, 12774, 12775, 12780, 12786, 12792, 12798, 14114, 14120, 14126, 14127, 14132, 14133, 14138, 14139, 14140, 14144, 14145, 14146, 14147, 14150, 14151, 14152, 14153, 14156, 14157, 14158, 14159, 14162, 14163, 14164, 14165, 14166, 14168, 14169, 14170, 14171, 14172, 14174, 14175, 14176, 14177, 14178, 14179, 14180, 14181, 14182, 14183, 14184, 14185, 14186, 14187, 14188, 14189, 14190, 14191, 14192, 14193, 14194, 14195, 14196, 14197, 14198, 14199, 14200, 14201, 14202, 14203, 14204, 14205, 14206, 14207, 14208, 14209, 14210, 14211, 14212, 14213, 14214, 14215, 14216, 14217, 14218, 14219, 14220, 14221, 14222, 14223, 14224, 14225, 14226, 14227, 14228, 14229, 14230, 14231, 14232, 14233, 14234, 14235, 14236, 14237, 14238, 14239, 14240, 14241, 14242, 14243, 14244, 14245, 14246, 14247, 14248, 14249, 14250, 14251, 14252, 14253, 14254, 14255, 14256, 14257, 14258, 14259, 14260, 14261, 14262, 14263, 14264, 14265, 14266, 14267, 14268, 14269, 14270, 14271, 14272, 14273, 14274, 14275, 14276, 14277, 14278, 14279, 14280, 14281, 14282, 14283, 14284, 14285, 14286, 14287, 14288, 14289, 14290, 14291, 14292, 14293, 14294, 14295, 14296, 14297, 14298, 14299, 14300, 14301, 14302, 14303, 14304, 14305, 14306, 14307, 14308, 14309, 14310, 14311, 14312, 14313, 14314, 14315, 14316, 14317, 14318, 14319, 14320, 14321, 14322, 14323, 14324, 14325, 14326, 14327, 14328, 14329, 14330, 14331, 14332, 14333, 14334, 14335, 14336, 14337, 14338, 14339, 14340, 14341, 14342, 14343, 14344, 14345, 14346, 14347, 14348, 14349, 14350, 14351, 14352, 14353, 14354, 14355, 14356, 14357, 14358, 14359, 14360, 14361, 14362, 14363, 14364, 14365, 14366, 14367, 14368, 14369, 14370, 14371, 14372, 14373, 14374, 14375, 14376, 14377, 14378, 14379, 14380, 14381, 14382, 14383, 14384, 14385, 14386, 14387, 14388, 14389, 14390, 14391, 14392, 14393, 14394, 14395, 14396, 14397, 14398, 14399, 14400, 14401, 14402, 14403, 14404, 14405, 14406, 14407, 14408, 14409, 14410, 14411, 14412, 14413, 14414, 14415, 14416, 14417, 14418, 14419, 14420, 14421, 14422, 14423, 14424, 14425, 14426, 14427, 14428, 14429, 14430, 14431, 14432, 14433, 14434, 14435, 14436, 14437, 14438, 14439, 14440, 14441, 14442, 14443, 14444, 14445, 14446, 14447, 14448, 14449, 14450, 14451, 14452, 14453, 14454, 14455, 14456, 14457, 14458, 14459, 14460, 14461, 14462, 14463, 14464, 14465, 14466, 14467, 14468, 14469, 14470, 14471, 14472, 14473, 14474, 14475, 14476, 14477, 14478, 14479, 14480, 14481, 14482, 14483, 14484, 14485, 14486, 14487, 14488, 14489, 14490, 14491, 14492, 14493, 14494, 14495, 14496, 14498, 14499, 14500, 14501, 14502, 14503, 14504, 62211, 62212, 62213, 62214, 62215, 62216, 62217, 62288, 62289, 62290, 62291, 62292, 62293, 62294, 62295, 62296, 62297, 62298, 62299, 62300, 62301, 62302, 62303, 62304, 62305, 62306, 62307, 62308, 62309, 62310, 62311, 62374, 62375, 62376, 62377, 62378, 62379, 62380, 62381, 62382, 62383, 62384, 62385, 62386, 62387, 62388, 62389, 62390, 62391, 62392, 62393, 62394, 62395, 62396, 62397, 62398, 62399, 62400, 62461, 62462, 62463, 62464, 62465, 62466, 62467, 62468, 62469, 62470, 62471, 62472, 62473, 62474, 62475, 62476, 62477, 62478, 62479, 62480, 62481, 62482, 62483, 62484, 62485, 62486, 62487, 62488, 62489, 62551, 62552, 62553, 62554, 62555, 62556, 62557, 62558, 62559, 62560, 62561, 62562, 62563, 62564, 62565, 62566, 62567, 62568, 62569, 62570, 62571, 62572, 62573, 62574, 62575, 62576, 62577, 62578, 62643, 62644, 62645, 62646, 62647, 62648, 62649, 62650, 62651, 62652, 62653, 62654, 62655, 62656, 62657, 62658, 62659, 62660, 62661, 62662, 62663, 62664, 62665, 62666, 62667, 62668, 62669, 62742, 62743, 62744, 62745, 62746, 62747, 62748, 62749, 62750, 62751, 62752, 62753, 62754, 62755, 62756, 62757, 62758, 62759, 62760, 62761, 62762, 62763, 62764, 62765, 62766, 62846, 62847, 62848, 62849, 62850, 62851, 62852, 62853, 62854, 62855, 62856, 62857, 62858, 62859, 62860, 62861, 62862, 62863, 62864, 62865, 62866, 62867, 62868, 62956, 62957, 62958, 62959, 62960, 62961, 62962, 62963, 62964, 62965, 62966, 62967, 62968, 62969, 62970, 62971, 62972, 62973, 62974, 62975, 62976, 63071, 63072, 63073, 63074, 63075, 63076, 63077, 63078, 63079, 63080, 63081, 63082, 63083, 63084, 63085, 63086, 63087, 63088, 63089, 63090, 63188, 63189, 63190, 63191, 63192, 63193, 63194, 63195, 63196, 63197, 63198, 63199, 63200, 63201, 63202, 63203, 63204, 63205, 63206, 63303, 63304, 63305, 63306, 63307, 63308, 63309, 63310, 63311, 63312, 63313, 63314, 63315, 63316, 63317, 63318, 63319, 63320, 63433, 63434, 63435, 63436, 63437, 63438, 63439, 63440, 63441, 63442, 63443, 63444, 63445, 63446, 63447, 63448, 63449, 63564, 63565, 63566, 63567, 63568, 63569, 63570, 63571, 63572, 63573, 63574, 63575, 63576, 63577, 63578, 63579, 63701, 63702, 63703, 63704, 63705, 63706, 63707, 63708, 63709, 63710, 63711, 63712, 63713, 63714, 63715, 63843, 63844, 63845, 63846, 63847, 63848, 63849, 63850, 63851, 63852, 63853, 63854, 63855, 63856, 63857, 63985, 63986, 63987, 63988, 63989, 63990, 63991, 63992, 63993, 63994, 63995, 63996, 63997, 63998, 63999, 64129, 64130, 64131, 64132, 64133, 64134, 64135, 64136, 64137, 64138, 64139, 64140, 64141, 64142, 64274, 64275, 64276, 64277, 64278, 64279, 64280, 64281, 64282, 64283, 64284, 64285, 64286, 64423, 64424, 64425, 64426, 64427, 64428, 64429, 64430, 64431, 64432, 64433, 64434, 64435, 64563, 64564, 64565, 64566, 64567, 64568, 64569, 64570, 64571, 64572, 64573, 64574, 64708, 64709, 64710, 64711, 64712, 64713, 64714, 64715, 64716, 64717, 64718, 64854, 64855, 64856, 64857, 64858, 64859, 64860, 64861, 64862, 64863, 64864, 64996, 64997, 64998, 64999, 65000, 65001, 65002, 65003, 65004, 65005, 65139, 65140, 65141, 65142, 65143, 65144, 65145, 65146, 65147, 65148, 65287, 65288, 65289, 65290, 65291, 65292, 65293, 65294, 65295, 65427, 65428, 65429, 65430, 65431, 65432, 65433, 65434, 65569, 65570, 65571, 65572, 65573, 65574, 65575, 65710, 65711, 65712, 65713, 65714, 65715, 65716, 65852, 65853, 65854, 65855, 65856, 65857, 65997, 65998, 65999, 66000, 66001, 66002, 66146, 66147, 66148, 66149, 66150, 66289, 66290, 69497, 69498, 69499, 69500, 69501, 69502, 69589, 69590, 69591, 69592, 69593, 69594, 69595, 69596, 69597, 69598, 69599, 69600, 69601, 69602, 69603, 69604, 69605, 69606, 69607, 69608, 69609, 69610, 69611, 69612, 69613, 69686, 69687, 69688, 69689, 69690, 69691, 69692, 69693, 69694, 69695, 69696, 69697, 69698, 69699, 69700, 69701, 69702, 69703, 69704, 69705, 69706, 69707, 69708, 69709, 69710, 69711, 69712, 69783, 69784, 69785, 69786, 69787, 69788, 69789, 69790, 69791, 69792, 69793, 69794, 69795, 69796, 69797, 69798, 69799, 69800, 69801, 69802, 69803, 69804, 69805, 69806, 69807, 69808, 69809, 69810, 69811, 69882, 69883, 69884, 69885, 69886, 69887, 69888, 69889, 69890, 69891, 69892, 69893, 69894, 69895, 69896, 69897, 69898, 69899, 69900, 69901, 69902, 69903, 69904, 69905, 69906, 69907, 69908, 69909, 69910, 69995, 69996, 69997, 69998, 69999, 70000, 70001, 70002, 70003, 70004, 70005, 70006, 70007, 70008, 70009, 70010, 70011, 70012, 70013, 70014, 70015, 70016, 70017, 70018, 70019, 70020, 70021, 70095, 70096, 70097, 70098, 70099, 70100, 70101, 70102, 70103, 70104, 70105, 70106, 70107, 70108, 70109, 70110, 70111, 70112, 70113, 70114, 70115, 70116, 70117, 70118, 70119, 70212, 70213, 70214, 70215, 70216, 70217, 70218, 70219, 70220, 70221, 70222, 70223, 70224, 70225, 70226, 70227, 70228, 70229, 70230, 70231, 70232, 70233, 70234, 70335, 70337, 70338, 70339, 70340, 70341, 70342, 70343, 70344, 70345, 70346, 70347, 70348, 70349, 70350, 70351, 70352, 70353, 70354, 70355, 70356, 70357, 70454, 70455, 70456, 70457, 70458, 70459, 70460, 70461, 70462, 70463, 70464, 70465, 70466, 70467, 70468, 70469, 70470, 70471, 70472, 70473, 70580, 70581, 70582, 70583, 70584, 70585, 70586, 70587, 70588, 70589, 70590, 70591, 70592, 70593, 70594, 70595, 70596, 70597, 70598, 70709, 70710, 70711, 70712, 70713, 70714, 70715, 70716, 70717, 70718, 70719, 70720, 70721, 70722, 70723, 70724, 70725, 70843, 70844, 70845, 70846, 70847, 70848, 70849, 70850, 70851, 70852, 70853, 70854, 70855, 70856, 70857, 70858, 70859, 70976, 70977, 70978, 70979, 70980, 70981, 70982, 70983, 70984, 70985, 70986, 70987, 70988, 70989, 70990, 70991, 71115, 71116, 71117, 71118, 71119, 71120, 71121, 71122, 71123, 71124, 71125, 71126, 71127, 71128, 71129, 71251, 71252, 71253, 71254, 71255, 71256, 71257, 71258, 71259, 71260, 71261, 71262, 71263, 71264, 71265, 71391, 71392, 71393, 71394, 71395, 71396, 71397, 71398, 71399, 71400, 71401, 71402, 71403, 71404, 71405, 71534, 71535, 71536, 71537, 71538, 71539, 71540, 71541, 71542, 71543, 71544, 71545, 71546, 71547, 71682, 71683, 71684, 71685, 71686, 71687, 71688, 71689, 71690, 71691, 71692, 71693, 71694, 71830, 71831, 71832, 71833, 71834, 71835, 71836, 71837, 71838, 71839, 71840, 71841, 71842, 71973, 71974, 71975, 71976, 71977, 71978, 71979, 71980, 71981, 71982, 71983, 71984, 72124, 72125, 72126, 72127, 72128, 72129, 72130, 72131, 72132, 72133, 72134, 72135, 72270, 72271, 72272, 72273, 72274, 72275, 72276, 72277, 72278, 72279, 72409, 72410, 72411, 72412, 72413, 72414, 72415, 72416, 72417, 72418, 72552, 72553, 72554, 72555, 72556, 72557, 72558, 72559, 72560, 72561, 72696, 72697, 72698, 72699, 72700, 72701, 72702, 72703, 72842, 72843, 72844, 72845, 72846, 72847, 72848, 72849, 72986, 72987, 72988, 72989, 72990, 72991, 72992, 73127, 73128, 73129, 73130, 73131, 73132, 73271, 73272, 73273, 73274, 73275, 73276, 73416, 73417, 73418, 73419, 73420, 73562, 73563, 73564, 73702, 80416, 80417, 80566, 80567, 80568, 80569, 80570, 80571, 80718, 80719, 80720, 80721, 80722, 80723, 80724, 80874, 80875, 80876, 80877, 80878, 80879, 80880, 80881, 81031, 81032, 81033, 81034, 81035, 81036, 81037, 81038, 81039, 81185, 81186, 81187, 81188, 81189, 81190, 81191, 81192, 81193, 81332, 81333, 81334, 81335, 81336, 81337, 81338, 81339, 81340, 81341, 81485, 81486, 81487, 81488, 81489, 81490, 81491, 81492, 81493, 81494, 81495, 81631, 81632, 81633, 81634, 81635, 81636, 81637, 81638, 81639, 81640, 81641, 81781, 81782, 81783, 81784, 81785, 81786, 81787, 81788, 81789, 81790, 81791, 81792, 81921, 81922, 81923, 81924, 81925, 81926, 81927, 81928, 81929, 81930, 81931, 81932, 82064, 82065, 82066, 82067, 82068, 82069, 82070, 82071, 82072, 82073, 82074, 82075, 82207, 82208, 82209, 82210, 82211, 82212, 82213, 82214, 82215, 82216, 82217, 82218, 82219, 82351, 82352, 82353, 82354, 82355, 82356, 82357, 82358, 82359, 82360, 82361, 82362, 82363, 82364, 82490, 82491, 82492, 82493, 82494, 82495, 82496, 82497, 82498, 82499, 82500, 82501, 82502, 82503, 82630, 82631, 82632, 82633, 82634, 82635, 82636, 82637, 82638, 82639, 82640, 82641, 82642, 82643, 82769, 82770, 82771, 82772, 82773, 82774, 82775, 82776, 82777, 82778, 82779, 82780, 82781, 82782, 82783, 82892, 82893, 82894, 82895, 82896, 82897, 82898, 82899, 82900, 82901, 82902, 82903, 82904, 82905, 82906, 82907, 83021, 83022, 83023, 83024, 83025, 83026, 83027, 83028, 83029, 83030, 83031, 83032, 83033, 83034, 83035, 83036, 83037, 83148, 83149, 83150, 83151, 83152, 83153, 83154, 83155, 83156, 83157, 83158, 83159, 83160, 83161, 83162, 83163, 83164, 83278, 83279, 83280, 83281, 83282, 83283, 83284, 83285, 83286, 83287, 83288, 83289, 83290, 83291, 83292, 83293, 83294, 83295, 83413, 83414, 83415, 83416, 83417, 83418, 83419, 83420, 83421, 83422, 83423, 83424, 83425, 83426, 83427, 83428, 83429, 83430, 83431, 83432, 83540, 83541, 83542, 83543, 83544, 83545, 83546, 83547, 83548, 83549, 83550, 83551, 83552, 83553, 83554, 83555, 83556, 83557, 83558, 83559, 83560, 83663, 83664, 83665, 83666, 83667, 83668, 83669, 83670, 83671, 83672, 83673, 83674, 83675, 83676, 83677, 83678, 83679, 83680, 83681, 83682, 83683, 83684, 83781, 83783, 83784, 83785, 83786, 83787, 83788, 83789, 83790, 83791, 83792, 83793, 83794, 83795, 83796, 83797, 83798, 83799, 83800, 83801, 83802, 83803, 83804, 83805, 83901, 83902, 83903, 83904, 83905, 83906, 83907, 83908, 83909, 83910, 83911, 83912, 83913, 83914, 83915, 83916, 83917, 83918, 83919, 83920, 83921, 83922, 83923, 83924, 83925, 84009, 84010, 84011, 84012, 84013, 84014, 84015, 84016, 84017, 84018, 84019, 84020, 84021, 84022, 84023, 84024, 84025, 84026, 84027, 84028, 84029, 84030, 84031, 84032, 84033, 84034, 84118, 84119, 84120, 84121, 84122, 84123, 84124, 84125, 84126, 84127, 84128, 84129, 84130, 84131, 84132, 84133, 84134, 84135, 84136, 84137, 84138, 84139, 84140, 84141, 84142, 84143, 84144, 84145, 84222, 84223, 84224, 84225, 84226, 84227, 84228, 84229, 84230, 84231, 84232, 84233, 84234, 84235, 84236, 84237, 84238, 84239, 84240, 84241, 84242, 84243, 84244, 84245, 84246, 84247, 84248, 84323, 84324, 84325, 84326, 84327, 84328, 84329, 84330, 84331, 84332, 84333, 84334, 84335, 84336, 84337, 84338, 84339, 84340, 84341, 84342, 84343, 84344, 84345, 84346, 84347, 84348, 84423, 84424, 84425, 84426, 84427, 84428, 84429, 84430, 84431, 84432, 84433, 84434, 84435, 84436, 84437, 84438, 84439, 84440, 84441, 84442, 84443, 84529, 84530, 84531, 88231, 88232, 88378, 88379, 88380, 88381, 88382, 88527, 88528, 88529, 88530, 88531, 88532, 88533, 88687, 88688, 88689, 88690, 88691, 88692, 88693, 88694, 88842, 88843, 88844, 88845, 88846, 88847, 88848, 88849, 88997, 88998, 88999, 89000, 89001, 89002, 89003, 89004, 89005, 89149, 89150, 89151, 89152, 89153, 89154, 89155, 89156, 89157, 89158, 89299, 89300, 89301, 89302, 89303, 89304, 89305, 89306, 89307, 89308, 89309, 89443, 89444, 89445, 89446, 89447, 89448, 89449, 89450, 89451, 89452, 89453, 89588, 89589, 89590, 89591, 89592, 89593, 89594, 89595, 89596, 89597, 89598, 89599, 89732, 89733, 89734, 89735, 89736, 89737, 89738, 89739, 89740, 89741, 89742, 89743, 89876, 89877, 89878, 89879, 89880, 89881, 89882, 89883, 89884, 89885, 89886, 89887, 90018, 90019, 90020, 90021, 90022, 90023, 90024, 90025, 90026, 90027, 90028, 90029, 90030, 90157, 90158, 90159, 90160, 90161, 90162, 90163, 90164, 90165, 90166, 90167, 90168, 90169, 90170, 90304, 90305, 90306, 90307, 90308, 90309, 90310, 90311, 90312, 90313, 90314, 90315, 90316, 90317, 90441, 90442, 90443, 90444, 90445, 90446, 90447, 90448, 90449, 90450, 90451, 90452, 90453, 90454, 90455, 90579, 90580, 90581, 90582, 90583, 90584, 90585, 90586, 90587, 90588, 90589, 90590, 90591, 90592, 90593, 90707, 90708, 90709, 90710, 90711, 90712, 90713, 90714, 90715, 90716, 90717, 90718, 90719, 90720, 90721, 90722, 90837, 90838, 90839, 90840, 90841, 90842, 90843, 90844, 90845, 90846, 90847, 90848, 90849, 90850, 90851, 90852, 90853, 90961, 90962, 90963, 90964, 90965, 90966, 90967, 90968, 90969, 90970, 90971, 90972, 90973, 90974, 90975, 90976, 90977, 91092, 91093, 91094, 91095, 91096, 91097, 91098, 91099, 91100, 91101, 91102, 91103, 91104, 91105, 91106, 91107, 91108, 91109, 91230, 91231, 91232, 91233, 91234, 91235, 91236, 91237, 91238, 91239, 91240, 91241, 91242, 91243, 91244, 91245, 91246, 91247, 91248, 91249, 91358, 91359, 91360, 91361, 91362, 91363, 91364, 91365, 91366, 91367, 91368, 91369, 91370, 91371, 91372, 91373, 91374, 91375, 91376, 91377, 91378, 91482, 91483, 91484, 91485, 91486, 91487, 91488, 91489, 91490, 91491, 91492, 91493, 91494, 91495, 91496, 91497, 91498, 91499, 91500, 91501, 91502, 91503, 91608, 91609, 91610, 91611, 91612, 91613, 91614, 91615, 91616, 91617, 91618, 91619, 91620, 91621, 91622, 91623, 91624, 91625, 91626, 91627, 91628, 91629, 91630, 91631, 91732, 91733, 91734, 91735, 91736, 91737, 91738, 91739, 91740, 91741, 91742, 91743, 91744, 91745, 91746, 91747, 91748, 91749, 91750, 91751, 91752, 91753, 91754, 91755, 91756, 91757, 91865, 91866, 91867, 91868, 91869, 91870, 91871, 91872, 91873, 91874, 91875, 91876, 91877, 91878, 91879, 91880, 91881, 91882, 91883, 91884, 91885, 91886, 91887, 91888, 91889, 91890, 91891, 91892, 91975, 91976, 91977, 91978, 91979, 91980, 91981, 91982, 91983, 91984, 91985, 91986, 91987, 91988, 91989, 91990, 91991, 91992, 91993, 91994, 91995, 91996, 91997, 91998, 91999, 92000, 92001, 92002, 92003, 92004, 92092, 92093, 92094, 92095, 92096, 92097, 92098, 92099, 92100, 92101, 92102, 92103, 92104, 92105, 92106, 92107, 92108, 92109, 92110, 92111, 92112, 92113, 92114, 92115, 92116, 92117, 92118, 92119, 92213, 92214, 92215, 92216, 92217, 92218, 92219, 92220, 92221, 92222, 92223, 92224, 92225, 92226, 92227, 92228, 92229, 92230, 92231, 92232, 92233, 92234, 92235, 92236, 92237, 92238, 92239, 92344, 92345, 92346, 92347, 92348, 92349, 92350, 92351, 92352, 92353, 92354, 92355, 92356, 92357, 92358, 92359, 92360, 92361, 92362, 92363, 125807, 125867, 125926, 125985, 126047, 126109, 126169, 126229, 126289, 126349, 126410, 126471, 126530, 126589, 126648, 126707, 126767, 126827, 126889, 126951, 127012, 127073, 127132, 127191, 127249, 127307, 127362, 127417, 127473, 127529, 127581, 127633, 127687, 127741, 127792, 127843, 127897, 127951, 128006, 128061, 128112, 128113, 128164, 128165, 128217, 128218, 128270, 128271, 128323, 128324, 128376, 128377, 128425, 128426, 128474, 128475, 128526, 128527, 128578, 128579, 128629, 128630, 128680, 128681, 128720, 128732, 128733, 128772, 128784, 128785, 128826, 128838, 128839, 128880, 128892, 128893, 128935, 128947, 128948, 128990, 129002, 129003, 129037, 129043, 129055, 129056, 129090, 129096, 129108, 129109, 129141, 129147, 129159, 129160, 129192, 129198, 129210, 129211, 129243, 129249, 129261, 129262, 129294, 129300, 129312, 129313, 129338, 129344, 129350, 129362, 129363, 129388, 129394, 129400, 129412, 129413, 129437, 129443, 129449, 129460, 129461, 129462, 129486, 129492, 129498, 129509, 129510, 129511, 129536, 129542, 129548, 129559, 129560, 129561, 129586, 129592, 129598, 129609, 129610, 129611, 129632, 129638, 129644, 129655, 129656, 129657, 129678, 129684, 129690, 129701, 129702, 129703, 129722, 129728, 129734, 129740, 129750, 129751, 129752, 129753, 129772, 129778, 129784, 129790, 129800, 129801, 129802, 129803, 129821, 129827, 129833, 129838, 129839, 129849, 129850, 129851, 129852, 129870, 129876, 129882, 129887, 129888, 129898, 129899, 129900, 129901, 129919, 129925, 129931, 129936, 129946, 129947, 129948, 129949, 129967, 129973, 129979, 129984, 129994, 129995, 129996, 129997, 130014, 130020, 130025, 130030, 130031, 130040, 130041, 130042, 130043, 130044, 130061, 130067, 130072, 130077, 130078, 130087, 130088, 130089, 130090, 130091, 130112, 130117, 130118, 130123, 130124, 130133, 130134, 130135, 130136, 130137, 130158, 130163, 130164, 130169, 130170, 130179, 130180, 130181, 130182, 130183, 130197, 130203, 130208, 130209, 130214, 130215, 130224, 130225, 130226, 130227, 130228, 130242, 130248, 130253, 130254, 130259, 130260, 130269, 130270, 130271, 130272, 130273, 130284, 130289, 130295, 130300, 130301, 130306, 130307, 130315, 130316, 130317, 130318, 130319, 130320, 130331, 130336, 130342, 130347, 130348, 130353, 130354, 130362, 130363, 130364, 130365, 130366, 130367, 130385, 130390, 130391, 130396, 130397, 130405, 130406, 130407, 130408, 130409, 130410, 130428, 130433, 130434, 130439, 130440, 130448, 130449, 130450, 130451, 130452, 130453, 130468, 130469, 130474, 130475, 130480, 130481, 130489, 130490, 130491, 130492, 130493, 130494, 130509, 130510, 130515, 130516, 130521, 130522, 130530, 130531, 130532, 130533, 130534, 130535, 130551, 130557, 130558, 130563, 130564, 130569, 130570, 130577, 130578, 130579, 130580, 130581, 130582, 130583, 130599, 130605, 130606, 130611, 130612, 130617, 130618, 130625, 130626, 130627, 130628, 130629, 130630, 130631, 130650, 130651, 130656, 130657, 130662, 130663, 130670, 130671, 130672, 130673, 130674, 130675, 130676, 130695, 130696, 130701, 130702, 130707, 130708, 130715, 130716, 130717, 130718, 130719, 130720, 130721, 130735, 130741, 130742, 130747, 130748, 130753, 130754, 130760, 130761, 130762, 130763, 130764, 130765, 130766, 130767, 130781, 130787, 130788, 130793, 130794, 130799, 130800, 130806, 130807, 130808, 130809, 130810, 130811, 130812, 130813, 130824, 130829, 130835, 130839, 130840, 130841, 130847, 130848, 130849, 130850, 130851, 130852, 130853, 130854, 130866, 130871, 130877, 130881, 130882, 130883, 130889, 130890, 130891, 130892, 130893, 130894, 130895, 130896, 130906, 130911, 130917, 130921, 130922, 130923, 130929, 130930, 130931, 130932, 130933, 130934, 130935, 130936, 130947, 130952, 130958, 130962, 130963, 130964, 130970, 130971, 130972, 130973, 130974, 130975, 130976, 130977, 130992, 130997, 130998, 131002, 131003, 131004, 131010, 131011, 131012, 131013, 131014, 131015, 131016, 131017, 131033, 131038, 131039, 131043, 131044, 131045, 131051, 131052, 131053, 131054, 131055, 131056, 131057, 131058, 131071, 131076, 131081, 131082, 131087, 131088, 131089, 131094, 131095, 131096, 131097, 131098, 131099, 131100, 131101, 131102, 131115, 131120, 131125, 131126, 131131, 131132, 131133, 131138, 131139, 131140, 131141, 131142, 131143, 131144, 131145, 131146, 131162, 131167, 131168, 131173, 131174, 131175, 131180, 131181, 131182, 131183, 131184, 131185, 131186, 131187, 131188, 131205, 131210, 131211, 131216, 131217, 131218, 131223, 131224, 131225, 131226, 131227, 131228, 131229, 131230, 131231, 131247, 131252, 131253, 131258, 131259, 131260, 131264, 131265, 131266, 131267, 131268, 131269, 131270, 131271, 131272, 131273, 131289, 131294, 131295, 131300, 131301, 131302, 131306, 131307, 131308, 131309, 131310, 131311, 131312, 131313, 131314, 131315, 131333, 131334, 131338, 131339, 131344, 131345, 131350, 131351, 131352, 131353, 131354, 131355, 131356, 131357, 131358, 131359, 131377, 131378, 131382, 131383, 131388, 131389, 131394, 131395, 131396, 131397, 131398, 131399, 131400, 131401, 131402, 131403, 131420, 131421, 131426, 131427, 131432, 131433, 131438, 131439, 131440, 131441, 131442, 131443, 131444, 131445, 131446, 131447, 131464, 131465, 131470, 131471, 131476, 131477, 131482, 131483, 131484, 131485, 131486, 131487, 131488, 131489, 131490, 131491, 131509, 131510, 131515, 131516, 131521, 131522, 131526, 131527, 131528, 131529, 131530, 131531, 131532, 131533, 131534, 131535, 131536, 131554, 131555, 131560, 131561, 131566, 131567, 131571, 131572, 131573, 131574, 131575, 131576, 131577, 131578, 131579, 131580, 131581, 131597, 131598, 131603, 131604, 131609, 131610, 131614, 131615, 131616, 131617, 131618, 131619, 131620, 131621, 131622, 131623, 131624, 131640, 131641, 131646, 131647, 131652, 131653, 131657, 131658, 131659, 131660, 131661, 131662, 131663, 131664, 131665, 131666, 131667, 131678, 131684, 131690, 131691, 131696, 131697, 131701, 131702, 131703, 131704, 131705, 131706, 131707, 131708, 131709, 131710, 131711, 131722, 131728, 131734, 131735, 131740, 131741, 131745, 131746, 131747, 131748, 131749, 131750, 131751, 131752, 131753, 131754, 131755, 131777, 131778, 131782, 131783, 131784, 131788, 131789, 131790, 131791, 131792, 131793, 131794, 131795, 131796, 131797, 131798, 131822, 131823, 131827, 131828, 131829, 131833, 131834, 131835, 131836, 131837, 131838, 131839, 131840, 131841, 131842, 131843, 131856, 131862, 131868, 131869, 131873, 131874, 131875, 131879, 131880, 131881, 131882, 131883, 131884, 131885, 131886, 131887, 131888, 131889, 131902, 131908, 131914, 131915, 131919, 131920, 131921, 131925, 131926, 131927, 131928, 131929, 131930, 131931, 131932, 131933, 131934, 131935, 131947, 131953, 131959, 131964, 131965, 131966, 131970, 131971, 131972, 131973, 131974, 131975, 131976, 131977, 131978, 131979, 131980, 131992, 131998, 132004, 132009, 132010, 132011, 132015, 132016, 132017, 132018, 132019, 132020, 132021, 132022, 132023, 132024, 132025, 132037, 132041, 132046, 132047, 132052, 132053, 132054, 132058, 132059, 132060, 132061, 132062, 132063, 132064, 132065, 132066, 132067, 132068, 132081, 132085, 132090, 132091, 132096, 132097, 132098, 132102, 132103, 132104, 132105, 132106, 132107, 132108, 132109, 132110, 132111, 132112, 132123, 132128, 132133, 132134, 132139, 132140, 132141, 132145, 132146, 132147, 132148, 132149, 132150, 132151, 132152, 132153, 132154, 132155, 132167, 132172, 132177, 132178, 132183, 132184, 132185, 132189, 132190, 132191, 132192, 132193, 132194, 132195, 132196, 132197, 132198, 132199, 132217, 132222, 132223, 132228, 132229, 132234, 132235, 132236, 132237, 132238, 132239, 132240, 132241, 132242, 132243, 132244, 132262, 132267, 132268, 132273, 132274, 132279, 132280, 132281, 132282, 132283, 132284, 132285, 132286, 132287, 132288, 132289, 132308, 132313, 132314, 132319, 132320, 132325, 132326, 132327, 132328, 132329, 132330, 132331, 132332, 132333, 132334, 132335, 132355, 132360, 132361, 132366, 132367, 132372, 132373, 132374, 132375, 132376, 132377, 132378, 132379, 132380, 132381, 132382, 132400, 132401, 132406, 132407, 132412, 132413, 132418, 132419, 132420, 132421, 132422, 132423, 132424, 132425, 132426, 132427, 132428, 132446, 132447, 132452, 132453, 132458, 132459, 132464, 132465, 132466, 132467, 132468, 132469, 132470, 132471, 132472, 132473, 132474, 132491, 132496, 132497, 132502, 132503, 132507, 132508, 132509, 132510, 132511, 132512, 132513, 132514, 132515, 132516, 132517, 132518, 132536, 132541, 132542, 132547, 132548, 132552, 132553, 132554, 132555, 132556, 132557, 132558, 132559, 132560, 132561, 132562, 132563, 132577, 132583, 132584, 132588, 132589, 132593, 132594, 132595, 132596, 132597, 132598, 132599, 132600, 132601, 132602, 132603, 132604, 132619, 132625, 132626, 132630, 132631, 132635, 132636, 132637, 132638, 132639, 132640, 132641, 132642, 132643, 132644, 132645, 132646, 132660, 132671, 132672, 132677, 132678, 132682, 132683, 132684, 132685, 132686, 132687, 132688, 132689, 132690, 132691, 132692, 132693, 132707, 132718, 132719, 132724, 132725, 132729, 132730, 132731, 132732, 132733, 132734, 132735, 132736, 132737, 132738, 132739, 132740, 132751, 132757, 132763, 132764, 132768, 132769, 132770, 132774, 132775, 132776, 132777, 132778, 132779, 132780, 132781, 132782, 132783, 132784, 132785, 132797, 132803, 132809, 132810, 132814, 132815, 132816, 132820, 132821, 132822, 132823, 132824, 132825, 132826, 132827, 132828, 132829, 132830, 132831, 132847, 132853, 132858, 132859, 132860, 132864, 132865, 132866, 132867, 132868, 132869, 132870, 132871, 132872, 132873, 132874, 132875, 132891, 132897, 132902, 132903, 132904, 132908, 132909, 132910, 132911, 132912, 132913, 132914, 132915, 132916, 132917, 132918, 132919, 132932, 132938, 132944, 132949, 132950, 132951, 132955, 132956, 132957, 132958, 132959, 132960, 132961, 132962, 132963, 132964, 132965, 132966, 132979, 132985, 132991, 132996, 132997, 132998, 133002, 133003, 133004, 133005, 133006, 133007, 133008, 133009, 133010, 133011, 133012, 133013, 133030, 133035, 133040, 133041, 133042, 133046, 133047, 133048, 133049, 133050, 133051, 133052, 133053, 133054, 133055, 133056, 133057, 133075, 133080, 133085, 133086, 133087, 133091, 133092, 133093, 133094, 133095, 133096, 133097, 133098, 133099, 133100, 133101, 133102, 133116, 133121, 133122, 133127, 133128, 133133, 133134, 133135, 133136, 133137, 133138, 133139, 133140, 133141, 133142, 133143, 133144, 133159, 133164, 133165, 133170, 133171, 133176, 133177, 133178, 133179, 133180, 133181, 133182, 133183, 133184, 133185, 133186, 133187, 133206, 133211, 133212, 133217, 133218, 133223, 133224, 133225, 133226, 133227, 133228, 133229, 133230, 133231, 133232, 133233, 133234, 133253, 133258, 133259, 133264, 133265, 133270, 133271, 133272, 133273, 133274, 133275, 133276, 133277, 133278, 133279, 133280, 133281, 133301, 133306, 133307, 133312, 133313, 133318, 133319, 133320, 133321, 133322, 133323, 133324, 133325, 133326, 133327, 133328, 133329, 133349, 133354, 133355, 133360, 133361, 133366, 133367, 133368, 133369, 133370, 133371, 133372, 133373, 133374, 133375, 133376, 133377, 133401, 133402, 133407, 133408, 133413, 133414, 133418, 133419, 133420, 133421, 133422, 133423, 133424, 133425, 133426, 133427, 133428, 133429, 133430, 133455, 133456, 133461, 133462, 133467, 133468, 133472, 133473, 133474, 133475, 133476, 133477, 133478, 133479, 133480, 133481, 133482, 133483, 133484, 133505, 133511, 133515, 133516, 133519, 133520, 133521, 133522, 133523, 133524, 133525, 133526, 133527, 133528, 133529, 133530, 133531, 133552, 133558, 133562, 133563, 133566, 133567, 133568, 133569, 133570, 133571, 133572, 133573, 133574, 133575, 133576, 133577, 133578, 133593, 133604, 133605, 133610, 133611, 133615, 133616, 133617, 133618, 133619, 133620, 133621, 133622, 133623, 133624, 133625, 133626, 133627, 133643, 133654, 133655, 133660, 133661, 133665, 133666, 133667, 133668, 133669, 133670, 133671, 133672, 133673, 133674, 133675, 133676, 133677, 133693, 133699, 133705, 133706, 133709, 133710, 133711, 133715, 133716, 133717, 133718, 133719, 133720, 133721, 133722, 133723, 133724, 133725, 133726, 133727, 133744, 133750, 133756, 133757, 133760, 133761, 133762, 133766, 133767, 133768, 133769, 133770, 133771, 133772, 133773, 133774, 133775, 133776, 133777, 133778, 133795, 133801, 133807, 133812, 133813, 133814, 133818, 133819, 133820, 133821, 133822, 133823, 133824, 133825, 133826, 133827, 133828, 133829, 133830, 133847, 133853, 133859, 133864, 133865, 133866, 133870, 133871, 133872, 133873, 133874, 133875, 133876, 133877, 133878, 133879, 133880, 133881, 133882, 133897, 133903, 133909, 133914, 133915, 133916, 133920, 133921, 133922, 133923, 133924, 133925, 133926, 133927, 133928, 133929, 133930, 133931, 133932, 133947, 133953, 133959, 133964, 133965, 133966, 133970, 133971, 133972, 133973, 133974, 133975, 133976, 133977, 133978, 133979, 133980, 133981, 133982, 134003, 134008, 134013, 134014, 134015, 134019, 134020, 134021, 134022, 134023, 134024, 134025, 134026, 134027, 134028, 134029, 134030, 134031, 134052, 134057, 134062, 134063, 134064, 134068, 134069, 134070, 134071, 134072, 134073, 134074, 134075, 134076, 134077, 134078, 134079, 134080, 134101, 134106, 134107, 134112, 134113, 134118, 134119, 134120, 134121, 134122, 134123, 134124, 134125, 134126, 134127, 134128, 134129, 134130, 134151, 134156, 134157, 134162, 134163, 134168, 134169, 134170, 134171, 134172, 134173, 134174, 134175, 134176, 134177, 134178, 134179, 134180, 134200, 134206, 134211, 134212, 134217, 134218, 134223, 134224, 134225, 134226, 134227, 134228, 134229, 134230, 134231, 134232, 134233, 134234, 134235, 134255, 134261, 134266, 134267, 134272, 134273, 134278, 134279, 134280, 134281, 134282, 134283, 134284, 134285, 134286, 134287, 134288, 134289, 134290, 134313, 134318, 134319, 134324, 134325, 134330, 134331, 134332, 134333, 134334, 134335, 134336, 134337, 134338, 134339, 134340, 134341, 134342, 134365, 134370, 134371, 134376, 134377, 134382, 134383, 134384, 134385, 134386, 134387, 134388, 134389, 134390, 134391, 134392, 134393, 134394, 134415, 134416, 134421, 134422, 134427, 134428, 134433, 134434, 134436, 134437, 134438, 134439, 134440, 134441, 134442, 134443, 134444, 134445, 134466, 134467, 134472, 134473, 134478, 134479, 134484, 134485, 134487, 134488, 134489, 134490, 134491, 134492, 134493, 134494, 134495, 134496, 134520, 134526, 134527, 134532, 134533, 134538, 134539, 134541, 134542, 134543, 134544, 134545, 134546, 134547, 134548, 134549, 134550, 134574, 134580, 134581, 134586, 134587, 134592, 134593, 134595, 134596, 134597, 134598, 134599, 134600, 134601, 134602, 134603, 134604, 134628, 134634, 134635, 134640, 134641, 134646, 134647, 134650, 134651, 134652, 134653, 134654, 134655, 134656, 134657, 134658, 134682, 134688, 134689, 134694, 134695, 134700, 134701, 134704, 134705, 134706, 134707, 134708, 134709, 134710, 134711, 134712, 134740, 134741, 134746, 134747, 134752, 134753, 134756, 134757, 134758, 134759, 134760, 134761, 134762, 134763, 134764, 134793, 134794, 134799, 134800, 134805, 134806, 134809, 134810, 134811, 134812, 134813, 134814, 134815, 134816, 134817, 134836, 134842, 134848, 134854, 134855, 134860, 134861, 134864, 134865, 134866, 134867, 134868, 134869, 134870, 134871, 134872, 134891, 134897, 134903, 134909, 134910, 134915, 134916, 134919, 134920, 134921, 134922, 134923, 134924, 134925, 134926, 134927, 134954, 134960, 134961, 134966, 134967, 134971, 134972, 134973, 134974, 134975, 134976, 134977, 134978, 135005, 135011, 135012, 135017, 135018, 135022, 135023, 135024, 135025, 135026, 135027, 135028, 135029, 135046, 135052, 135057, 135063, 135064, 135069, 135070, 135074, 135075, 135076, 135077, 135078, 135079, 135080, 135081, 135098, 135104, 135109, 135115, 135116, 135121, 135122, 135126, 135127, 135128, 135129, 135130, 135131, 135132, 135133, 135155, 135161, 135167, 135173, 135174, 135178, 135179, 135180, 135181, 135182, 135183, 135184, 135185, 135207, 135213, 135219, 135225, 135226, 135230, 135231, 135232, 135233, 135234, 135235, 135236, 135237, 135262, 135267, 135273, 135279, 135280, 135285, 135286, 135287, 135288, 135289, 135290, 135291, 135310, 135316, 135321, 135327, 135333, 135334, 135339, 135340, 135341, 135342, 135343, 135344, 135345, 135371, 135381, 135387, 135388, 135393, 135394, 135395, 135396, 135397, 135398, 135399, 135425, 135435, 135441, 135442, 135447, 135448, 135449, 135450, 135451, 135452, 135453, 135486, 135492, 135498, 135504, 135505, 135506, 135507, 135508, 135509, 135510, 135543, 135549, 135555, 135561, 135562, 135563, 135564, 135565, 135566, 135567, 135601, 135607, 135613, 135620, 135621, 135622, 135623, 135624, 135625, 135659, 135665, 135671, 135678, 135679, 135680, 135681, 135682, 135683, 135716, 135722, 135728, 135735, 135736, 135737, 135738, 135739, 135740, 135773, 135779, 135785, 135792, 135793, 135794, 135795, 135796, 135797, 135830, 135836, 135842, 135850, 135851, 135852, 135853, 135854, 135888, 135894, 135900, 135908, 135909, 135910, 135911, 135912, 135951, 135957, 135965, 135966, 135967, 135968, 135969, 136009, 136015, 136023, 136024, 136025, 136026, 136027, 136067, 136073, 136081, 136082, 136083, 136084, 136085, 136125, 136131, 136139, 136140, 136141, 136142, 136143, 136182, 136188, 136197, 136198, 136199, 136200, 136239, 136245, 136254, 136255, 136256, 136257, 136300, 136309, 136310, 136311, 136312, 136355, 136364, 136365, 136366, 136367, 136414, 136423, 136424, 136425, 136426, 136473, 136482, 136483, 136484, 136485, 136531, 136541, 136542, 136543, 136589, 136599, 136600, 136601, 136659, 136660, 136661, 136719, 136720, 136721, 136779, 136780, 136781, 136839, 136840, 136841, 136901, 136902, 136962, 136963, 137025, 137026, 137088, 137089, 137149, 137150, 137210, 137211, 137271, 137272, 137332, 137333, 137394, 137395, 137456, 137457, 137519, 137520, 137582, 137583, 137644, 137645, 137706, 137707, 137765, 137766, 137824, 137825, 137884, 137885, 137944, 137945, 138004, 138005, 138064, 138065, 138123, 138124, 138182, 138183, 138242, 138243, 138302, 138303, 138362, 138363, 138422, 138423, 138483, 138484, 138544, 138545, 138605, 138665, 138726, 138787, 138848, 138909, 138970, 139031, 139092, 139153, 139214, 139275, 139336, 139397, 139456, 139515, 139575, 139635, 139696, 139757, 139819, 139881, 139943, 140005, 140067, 140129, 140191, 140253, 140315, 140377, 140439, 140501, 140563, 140625, 140687, 140749, 140811, 140873], "numberOfPointsInBox": 6792}, "attributes": {"trackId": "ego_track"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "23b469d0-d2c0-4158-9794-29442f5288c1", "objectId": "fa0f2957-29a5-404c-95f1-45c44ff58103", "className": "3D_track", "geometry": {"associatedPoints": [2567, 2579, 2596, 2601, 2606, 2607, 2608, 2612, 2617, 2622, 2623, 2627, 2628, 2632, 2633, 2638, 2639, 2642, 2643, 2644, 2648, 2649, 2650, 2654, 2655, 2656, 2659, 2660, 2661, 2665, 2666, 2667, 2671, 2672, 2673, 2676, 2677, 2678, 2682, 2683, 2684, 2688, 2689, 2690, 2694, 2695, 2696, 2700, 2701, 2702, 2704, 2705, 2706, 2708, 2709, 2710, 2714, 2715, 2716, 2720, 2721, 2725, 2726, 2731, 2732, 2736, 2737, 2741, 2742, 2745, 2749, 2754, 2760, 2766, 2772, 5716, 5719, 5724, 5726, 5728, 5730, 5734, 5737, 5740, 5744, 5746, 7349, 7355, 7361, 7366, 7372, 7382, 7387, 7388, 7393, 7398, 7406, 7411, 7416, 10266, 10272, 10278, 10284, 10289, 10290, 10295, 10296, 10301, 10302, 10307, 10308, 10313, 10314, 10318, 10319, 10320, 10324, 10325, 10326, 10330, 10331, 10332, 10336, 10337, 10338, 10342, 10343, 10344, 10347, 10348, 10349, 10352, 10353, 10354, 10358, 10359, 10360, 10363, 10364, 10365, 10369, 10370, 10371, 10375, 10376, 10381, 10382, 10386, 10387, 10388, 10391, 10392, 10393, 10397, 10398, 10399, 10402, 10403, 10404, 10407, 10408, 10411, 10412, 10415, 10416, 10418, 10422, 10423, 10426, 10427, 10429, 10430, 10431, 10433, 10434, 10436, 10437, 10439, 10441, 10444, 10449, 10453, 10455, 10457, 10459, 13157, 13163, 13169, 13175, 13180, 13181, 13186, 13187, 13192, 13193, 13198, 13199, 13203, 13204, 13205, 13209, 13210, 13211, 13215, 13216, 13217, 13221, 13222, 13223, 13227, 13228, 13229, 13232, 13233, 13234, 13235, 13238, 13239, 13240, 13241, 13244, 13245, 13246, 13247, 13250, 13251, 13252, 13253, 13256, 13257, 13258, 13261, 13262, 13263, 13264, 13267, 13268, 13269, 13270, 13273, 13274, 13275, 13276, 13279, 13280, 13281, 13282, 13284, 13285, 13286, 13287, 13290, 13291, 13292, 13293, 13296, 13297, 13298, 13299, 13302, 13303, 13304, 13305, 13308, 13309, 13310, 13311, 13314, 13315, 13316, 13320, 13321, 13322, 13326, 13327, 13328, 13332, 13333, 13334, 13338, 13339, 13340, 13344, 13345, 13350, 13351, 13355, 13356, 13360, 13361, 13365, 13366, 13370, 13371, 13376, 13381, 13386, 13391, 13395, 13399, 13404, 16049, 16055, 16061, 16067, 16073, 16079, 16084, 16085, 16090, 16091, 16096, 16097, 16102, 16103, 16108, 16109, 16113, 16114, 16115, 16119, 16120, 16121, 16125, 16126, 16127, 16131, 16132, 16133, 16137, 16138, 16139, 16142, 16143, 16144, 16145, 16148, 16149, 16150, 16151, 16154, 16155, 16156, 16157, 16160, 16161, 16162, 16163, 16165, 16166, 16167, 16168, 16169, 16171, 16172, 16173, 16174, 16177, 16178, 16179, 16180, 16183, 16184, 16185, 16186, 16189, 16190, 16191, 16192, 16194, 16195, 16196, 16197, 16198, 16200, 16201, 16202, 16203, 16206, 16207, 16208, 16209, 16212, 16213, 16214, 16215, 16218, 16219, 16220, 16221, 16224, 16225, 16226, 16230, 16231, 16232, 16236, 16237, 16238, 16242, 16243, 16244, 16248, 16249, 16250, 16254, 16255, 16260, 16261, 16266, 16267, 16272, 16273, 16278, 16279, 16284, 16290, 16296, 16302, 16308, 17698, 17704, 17710, 17711, 17716, 17717, 17722, 17723, 17728, 17729, 17730, 17734, 17735, 17736, 17740, 17741, 17742, 17743, 17746, 17747, 17748, 17749, 17752, 17753, 17754, 17755, 17758, 17759, 17760, 17761, 17762, 17764, 17765, 17766, 17767, 17768, 17770, 17771, 17772, 17773, 17774, 17776, 17777, 17778, 17779, 17780, 17781, 17782, 17783, 17784, 17785, 17786, 17787, 17788, 17789, 17790, 17791, 17792, 17793, 17794, 17795, 17796, 17797, 17798, 17799, 17800, 17801, 17802, 17803, 17804, 17805, 17806, 17807, 17808, 17809, 17810, 17811, 17813, 17814, 17815, 17816, 17817, 17819, 17820, 17821, 17822, 17823, 17825, 17826, 17827, 17828, 17829, 17832, 17833, 17834, 17835, 17838, 17839, 17840, 17841, 17845, 17846, 17847, 17851, 17852, 17853, 17857, 17858, 17859, 17864, 17865, 17870, 17871, 17876, 17877, 17883, 17889, 17895, 17901, 18285, 18291, 18297, 18303, 18308, 18309, 18314, 18315, 18320, 18321, 18325, 18326, 18327, 18331, 18332, 18333, 18337, 18338, 18339, 18342, 18343, 18344, 18345, 18348, 18349, 18350, 18351, 18354, 18355, 18356, 18357, 18359, 18360, 18361, 18362, 18365, 18366, 18367, 18368, 18371, 18372, 18373, 18374, 18376, 18377, 18378, 18379, 18382, 18383, 18384, 18385, 18388, 18389, 18390, 18391, 18394, 18395, 18396, 18400, 18401, 18402, 18406, 18407, 18408, 18412, 18413, 18414, 18418, 18419, 18424, 18425, 18430, 18431, 18435, 18440, 18445, 22120, 22126, 22132, 22138, 22139, 22144, 22145, 22150, 22151, 22152, 22156, 22157, 22158, 22162, 22163, 22164, 22168, 22169, 22170, 22171, 22174, 22175, 22176, 22177, 22180, 22181, 22182, 22183, 22184, 22186, 22187, 22188, 22189, 22190, 22192, 22193, 22194, 22195, 22196, 22198, 22199, 22200, 22201, 22202, 22203, 22204, 22205, 22206, 22207, 22208, 22209, 22210, 22211, 22212, 22213, 22214, 22215, 22216, 22217, 22218, 22219, 22220, 22221, 22223, 22224, 22225, 22226, 22227, 22229, 22230, 22231, 22232, 22233, 22236, 22237, 22238, 22239, 22242, 22243, 22244, 22245, 22248, 22249, 22250, 22251, 22255, 22256, 22257, 22261, 22262, 22263, 22267, 22268, 22269, 22274, 22275, 22280, 22281, 22286, 22287, 22293, 22299, 22305, 22311, 22677, 22683, 22689, 22695, 22700, 22701, 22706, 22707, 22712, 22713, 22717, 22718, 22719, 22723, 22724, 22725, 22729, 22730, 22731, 22735, 22736, 22737, 22740, 22741, 22742, 22746, 22747, 22748, 22752, 22753, 22754, 22757, 22758, 22759, 22760, 22763, 22764, 22765, 22769, 22770, 22771, 22774, 22775, 22776, 22777, 22780, 22781, 22782, 22786, 22787, 22788, 22791, 22792, 22793, 22796, 22797, 22798, 22801, 22802, 22806, 22807, 22811, 22812, 22815, 22819, 22824, 23581, 23587, 23593, 23599, 23605, 23611, 23612, 23617, 23618, 23623, 23624, 23629, 23630, 23635, 23636, 23641, 23642, 23643, 23647, 23648, 23649, 23653, 23654, 23655, 23659, 23660, 23661, 23665, 23666, 23667, 23671, 23672, 23673, 23677, 23678, 23679, 23680, 23683, 23684, 23685, 23686, 23689, 23690, 23691, 23692, 23695, 23696, 23697, 23698, 23701, 23702, 23703, 23704, 23707, 23708, 23709, 23710, 23713, 23714, 23715, 23716, 23719, 23720, 23721, 23722, 23725, 23726, 23727, 23728, 23731, 23732, 23733, 23734, 23735, 23737, 23738, 23739, 23740, 23741, 23743, 23744, 23745, 23746, 23747, 23749, 23750, 23751, 23752, 23753, 23755, 23756, 23757, 23758, 23759, 23761, 23762, 23763, 23764, 23765, 23767, 23768, 23769, 23770, 23771, 23773, 23774, 23775, 23776, 23777, 23779, 23780, 23781, 23782, 23783, 23785, 23786, 23787, 23788, 23789, 23791, 23792, 23793, 23794, 23795, 23797, 23798, 23799, 23800, 23801, 23803, 23804, 23805, 23806, 23807, 23809, 23810, 23811, 23812, 23813, 23815, 23816, 23817, 23818, 23819, 23821, 23822, 23823, 23824, 23825, 23827, 23828, 23829, 23830, 23831, 23833, 23834, 23835, 23836, 23837, 23839, 23840, 23841, 23842, 23843, 23845, 23846, 23847, 23848, 23849, 23851, 23852, 23853, 23854, 23857, 23858, 23859, 23860, 23863, 23864, 23865, 23866, 23869, 23870, 23871, 23872, 23875, 23876, 23877, 23878, 23881, 23882, 23883, 23884, 23887, 23888, 23889, 23890, 23893, 23894, 23895, 23896, 23899, 23900, 23901, 23902, 23905, 23906, 23907, 23911, 23912, 23913, 23917, 23918, 23919, 23923, 23924, 23925, 23929, 23930, 23931, 23935, 23936, 23937, 23941, 23942, 23947, 23948, 23953, 23954, 23959, 23960, 23965, 23966, 23971, 23977, 23983, 23989, 26552, 26558, 26564, 26565, 26570, 26571, 26576, 26577, 26582, 26583, 26584, 26588, 26589, 26590, 26594, 26595, 26596, 26600, 26601, 26602, 26603, 26606, 26607, 26608, 26609, 26612, 26613, 26614, 26615, 26618, 26619, 26620, 26621, 26622, 26624, 26625, 26626, 26627, 26628, 26630, 26631, 26632, 26633, 26634, 26636, 26637, 26638, 26639, 26640, 26641, 26642, 26643, 26644, 26645, 26646, 26647, 26649, 26650, 26651, 26652, 26653, 26655, 26656, 26657, 26658, 26659, 26661, 26662, 26663, 26664, 26665, 26668, 26669, 26670, 26671, 26674, 26675, 26676, 26677, 26680, 26681, 26682, 26683, 26687, 26688, 26689, 26693, 26694, 26695, 26699, 26700, 26701, 26706, 26707, 26712, 26713, 26718, 26719, 26724, 26725, 26731, 26737, 26743, 26749, 27019, 27025, 27031, 27037, 27043, 27048, 27053, 27054, 27059, 27060, 27065, 27066, 27070, 27071, 27072, 27076, 27077, 27082, 27083, 27087, 27088, 27091, 27092, 27093, 27097, 27098, 27102, 27103, 27107, 27108, 27111, 27112, 27113, 27117, 27118, 27121, 27122, 27124, 27125, 27126, 27128, 27129, 27130, 27133, 27134, 27137, 27138, 27140, 27141, 27144, 27145, 27149, 27153, 27156, 27159, 27832, 27838, 27844, 27850, 27851, 27856, 27857, 27862, 27863, 27868, 27869, 27874, 27875, 27876, 27880, 27881, 27882, 27886, 27887, 27888, 27892, 27893, 27894, 27898, 27899, 27900, 27901, 27904, 27905, 27906, 27907, 27910, 27911, 27912, 27913, 27916, 27917, 27918, 27919, 27922, 27923, 27924, 27925, 27926, 27928, 27929, 27930, 27931, 27932, 27934, 27935, 27936, 27937, 27938, 27940, 27941, 27942, 27943, 27944, 27946, 27947, 27948, 27949, 27950, 27951, 27952, 27953, 27954, 27955, 27956, 27957, 27958, 27959, 27960, 27961, 27962, 27963, 27964, 27965, 27966, 27967, 27968, 27969, 27970, 27971, 27972, 27973, 27974, 27975, 27976, 27977, 27978, 27979, 27980, 27981, 27982, 27983, 27984, 27985, 27986, 27987, 27988, 27989, 27990, 27991, 27992, 27993, 27994, 27995, 27996, 27997, 27998, 27999, 28000, 28001, 28002, 28003, 28004, 28005, 28006, 28007, 28008, 28009, 28010, 28011, 28012, 28013, 28014, 28015, 28016, 28017, 28018, 28019, 28020, 28021, 28022, 28023, 28024, 28025, 28026, 28027, 28028, 28029, 28030, 28031, 28032, 28033, 28034, 28035, 28036, 28037, 28038, 28039, 28040, 28041, 28042, 28043, 28044, 28045, 28046, 28047, 28048, 28049, 28050, 28051, 28052, 28053, 28054, 28055, 28056, 28057, 28058, 28059, 28060, 28061, 28062, 28063, 28064, 28065, 28066, 28067, 28068, 28069, 28070, 28071, 28072, 28073, 28074, 28075, 28076, 28077, 28078, 28079, 28080, 28081, 28082, 28083, 28084, 28085, 28086, 28087, 28088, 28089, 28090, 28091, 28092, 28093, 28094, 28095, 28096, 28097, 28098, 28099, 28100, 28101, 28103, 28104, 28105, 28106, 28107, 28109, 28110, 28111, 28112, 28113, 28115, 28116, 28117, 28118, 28119, 28120, 28121, 28122, 28123, 28124, 28125, 28126, 28127, 28128, 28129, 28130, 28131, 28133, 28134, 28135, 28136, 28137, 28139, 28140, 28141, 28142, 28143, 28145, 28146, 28147, 28148, 28149, 28151, 28152, 28153, 28154, 28155, 28157, 28158, 28159, 28160, 28161, 28163, 28164, 28165, 28166, 28167, 28169, 28170, 28171, 28172, 28173, 28175, 28176, 28177, 28178, 28179, 28181, 28182, 28183, 28184, 28185, 28187, 28188, 28189, 28190, 28191, 28193, 28194, 28195, 28196, 28197, 28199, 28200, 28201, 28202, 28203, 28205, 28206, 28207, 28208, 28209, 28211, 28212, 28213, 28214, 28215, 28217, 28218, 28219, 28220, 28221, 28222, 28223, 28224, 28225, 28226, 28227, 28228, 28229, 28230, 28231, 28232, 28233, 28234, 28235, 28236, 28237, 28238, 28239, 28240, 28241, 28242, 28243, 28244, 28245, 28246, 28247, 28248, 28249, 28250, 28251, 28252, 28253, 28254, 28255, 28256, 28257, 28258, 28259, 28260, 28261, 28262, 28263, 28264, 28265, 28266, 28267, 28268, 28269, 28270, 28271, 28272, 28273, 28274, 28275, 28276, 28277, 28278, 28279, 28280, 28281, 28282, 28283, 28284, 28285, 28286, 28287, 28288, 28289, 28290, 28291, 28292, 28293, 28294, 28295, 28296, 28297, 28298, 28299, 28300, 28301, 28302, 28303, 28304, 28305, 28306, 28307, 28308, 28309, 28310, 28312, 28313, 28314, 28315, 28316, 28318, 28319, 28320, 28321, 28322, 28324, 28325, 28326, 28327, 28328, 28330, 28331, 28332, 28333, 28336, 28337, 28338, 28339, 28342, 28343, 28344, 28345, 28348, 28349, 28350, 28351, 28354, 28355, 28356, 28360, 28361, 28362, 28366, 28367, 28368, 28372, 28373, 28374, 28378, 28379, 28384, 28385, 28390, 28391, 28396, 28402, 28408, 30964, 30970, 30976, 30982, 30983, 30988, 30989, 30994, 30995, 31000, 31001, 31002, 31006, 31007, 31008, 31012, 31013, 31014, 31018, 31019, 31020, 31024, 31025, 31026, 31027, 31030, 31031, 31032, 31033, 31036, 31037, 31038, 31039, 31042, 31043, 31044, 31045, 31048, 31049, 31050, 31051, 31054, 31055, 31056, 31057, 31058, 31060, 31061, 31062, 31063, 31064, 31067, 31068, 31069, 31070, 31073, 31074, 31075, 31076, 31079, 31080, 31081, 31082, 31085, 31086, 31087, 31088, 31089, 31092, 31093, 31094, 31095, 31098, 31099, 31100, 31101, 31104, 31105, 31106, 31107, 31110, 31111, 31112, 31113, 31116, 31117, 31118, 31119, 31123, 31124, 31125, 31129, 31130, 31131, 31135, 31136, 31137, 31141, 31142, 31143, 31147, 31148, 31149, 31153, 31154, 31155, 31160, 31161, 31166, 31167, 31172, 31173, 31178, 31179, 31184, 31185, 31190, 31191, 31196, 31197, 31202, 31203, 31208, 31209, 31214, 31215, 31220, 31221, 31226, 31227, 31232, 31233, 31238, 31239, 31244, 31245, 31250, 31251, 31256, 31257, 31262, 31263, 31268, 31269, 31274, 31275, 31280, 31281, 31286, 31287, 31292, 31297, 31302, 31314, 31318, 31323, 31327, 31346, 31350, 31353, 31362, 31365, 31372, 31374, 31378, 31994, 32000, 32006, 32012, 32013, 32018, 32019, 32024, 32025, 32030, 32031, 32036, 32037, 32038, 32042, 32043, 32044, 32048, 32049, 32050, 32054, 32055, 32056, 32057, 32060, 32061, 32062, 32063, 32066, 32067, 32068, 32069, 32072, 32073, 32074, 32075, 32076, 32078, 32079, 32080, 32081, 32082, 32084, 32085, 32086, 32087, 32088, 32090, 32091, 32092, 32093, 32094, 32096, 32097, 32098, 32099, 32100, 32101, 32102, 32103, 32104, 32105, 32106, 32107, 32108, 32109, 32110, 32111, 32112, 32113, 32114, 32115, 32116, 32117, 32118, 32119, 32120, 32121, 32122, 32123, 32124, 32125, 32126, 32127, 32128, 32129, 32130, 32131, 32132, 32133, 32134, 32135, 32136, 32137, 32138, 32139, 32140, 32141, 32142, 32143, 32144, 32145, 32146, 32147, 32148, 32149, 32150, 32151, 32152, 32153, 32154, 32155, 32156, 32157, 32158, 32159, 32160, 32161, 32162, 32163, 32164, 32165, 32166, 32167, 32168, 32169, 32170, 32171, 32172, 32173, 32174, 32175, 32176, 32177, 32178, 32179, 32181, 32182, 32183, 32184, 32185, 32187, 32188, 32189, 32190, 32191, 32193, 32194, 32195, 32196, 32197, 32199, 32200, 32201, 32202, 32203, 32205, 32206, 32207, 32208, 32209, 32211, 32212, 32213, 32214, 32215, 32218, 32219, 32220, 32221, 32224, 32225, 32226, 32227, 32230, 32231, 32232, 32233, 32237, 32238, 32239, 32243, 32244, 32245, 32249, 32250, 32251, 32255, 32256, 32257, 32261, 32262, 32263, 32268, 32269, 32274, 32275, 32280, 32281, 32286, 32287, 32292, 32293, 32298, 32299, 32304, 32305, 32310, 32311, 32316, 32317, 32322, 32323, 32328, 32329, 32335, 32341, 32347, 32353, 32359, 32365, 32371, 32377, 32383, 32389, 32395, 32401, 32407, 32413, 32419, 32425, 32431, 32437, 32443, 32448, 32449, 32454, 32455, 32460, 32461, 32466, 32467, 32472, 32473, 32478, 32479, 32484, 32485, 32490, 32491, 32496, 32497, 32501, 32502, 32503, 32507, 32508, 32509, 32513, 32514, 32515, 32519, 32520, 32521, 32524, 32525, 32526, 32527, 32530, 32531, 32532, 32533, 32536, 32537, 32538, 32539, 32542, 32543, 32544, 32545, 32548, 32549, 32550, 32551, 32553, 32554, 32555, 32556, 32557, 32559, 32560, 32561, 32562, 32563, 32565, 32566, 32567, 32568, 32569, 32571, 32572, 32573, 32574, 32575, 32576, 32577, 32578, 32579, 32580, 32581, 32582, 32583, 32584, 32585, 32586, 32587, 32588, 32589, 32590, 32591, 32592, 32593, 32594, 32595, 32596, 32597, 32598, 32600, 32601, 32602, 32603, 32604, 32606, 32607, 32608, 32609, 32610, 32612, 32613, 32614, 32615, 32618, 32619, 32620, 32621, 32624, 32625, 32626, 32627, 32630, 32631, 32632, 32636, 32637, 32638, 32642, 32643, 32644, 32648, 32649, 32654, 32655, 32660, 32661, 32666, 32672, 32678, 35260, 35265, 35270, 35274, 35279, 35285, 35286, 35290, 35291, 35295, 35296, 35299, 35300, 35304, 35305, 35309, 35310, 35314, 35315, 35318, 35319, 35323, 35324, 35328, 35329, 35333, 35334, 35335, 35337, 35338, 35339, 35341, 35342, 35343, 35346, 35347, 35348, 35352, 35353, 35354, 35358, 35359, 35360, 35364, 35365, 35366, 35369, 35370, 35371, 35375, 35376, 35377, 35380, 35381, 35382, 35385, 35386, 35387, 35390, 35391, 35392, 35395, 35396, 35397, 35400, 35401, 35402, 35405, 35406, 35407, 35409, 35410, 35413, 35414, 35417, 35418, 35420, 35421, 35423, 35424, 35426, 35429, 35434, 36131, 36137, 36143, 36149, 36150, 36155, 36156, 36161, 36162, 36163, 36167, 36168, 36169, 36173, 36174, 36175, 36179, 36180, 36181, 36182, 36185, 36186, 36187, 36188, 36191, 36192, 36193, 36194, 36197, 36198, 36199, 36200, 36201, 36203, 36204, 36205, 36206, 36207, 36209, 36210, 36211, 36212, 36213, 36215, 36216, 36217, 36218, 36219, 36220, 36221, 36222, 36223, 36224, 36225, 36226, 36227, 36228, 36229, 36230, 36231, 36232, 36233, 36234, 36235, 36236, 36237, 36238, 36239, 36240, 36241, 36242, 36243, 36244, 36245, 36246, 36247, 36248, 36249, 36250, 36251, 36252, 36253, 36254, 36255, 36256, 36257, 36258, 36259, 36260, 36261, 36262, 36263, 36264, 36265, 36266, 36267, 36268, 36269, 36270, 36271, 36272, 36273, 36274, 36275, 36276, 36277, 36278, 36279, 36280, 36282, 36283, 36284, 36285, 36286, 36288, 36289, 36290, 36291, 36292, 36295, 36296, 36297, 36298, 36301, 36302, 36303, 36304, 36307, 36308, 36309, 36310, 36313, 36314, 36315, 36316, 36320, 36321, 36322, 36326, 36327, 36328, 36332, 36333, 36334, 36339, 36340, 36345, 36346, 36351, 36352, 36357, 36358, 36363, 36364, 36370, 36376, 36382, 36388, 36682, 36688, 36694, 36700, 36705, 36706, 36711, 36712, 36717, 36718, 36723, 36724, 36728, 36729, 36730, 36734, 36735, 36736, 36740, 36741, 36742, 36745, 36746, 36747, 36748, 36751, 36752, 36753, 36754, 36757, 36758, 36759, 36760, 36763, 36764, 36765, 36766, 36768, 36769, 36770, 36771, 36772, 36774, 36775, 36776, 36777, 36778, 36780, 36781, 36782, 36783, 36784, 36785, 36786, 36787, 36788, 36789, 36791, 36792, 36793, 36794, 36795, 36797, 36798, 36799, 36800, 36801, 36803, 36804, 36805, 36806, 36809, 36810, 36811, 36812, 36815, 36816, 36817, 36818, 36821, 36822, 36823, 36827, 36828, 36829, 36833, 36834, 36835, 36839, 36840, 36845, 36846, 36851, 36857, 36863, 62495, 62496, 62497, 62498, 62589, 62590, 62591, 62592, 62593, 62594, 62595, 62691, 62692, 62693, 62694, 62695, 62696, 62697, 62698, 62699, 62798, 62799, 62800, 62801, 62802, 62803, 62804, 62805, 62806, 62807, 62908, 62909, 62910, 62911, 62912, 62913, 62914, 62915, 62916, 62917, 62918, 62919, 62920, 63027, 63028, 63029, 63030, 63031, 63032, 63033, 63034, 63035, 63036, 63037, 63038, 63039, 63145, 63147, 63148, 63149, 63150, 63151, 63152, 63153, 63154, 63155, 63156, 63157, 63158, 63159, 63263, 63264, 63265, 63266, 63267, 63268, 63269, 63270, 63271, 63272, 63273, 63274, 63275, 63276, 63396, 63397, 63398, 63399, 63400, 63401, 63402, 63403, 63404, 63405, 63406, 63407, 63408, 63528, 63529, 63530, 63531, 63532, 63533, 63534, 63535, 63536, 63537, 63538, 63539, 63540, 63667, 63668, 63669, 63670, 63671, 63672, 63673, 63674, 63675, 63676, 63677, 63678, 63679, 63810, 63811, 63812, 63813, 63814, 63815, 63816, 63817, 63818, 63819, 63820, 63821, 63953, 63955, 63956, 63957, 63958, 63959, 63960, 63961, 63962, 63963, 63964, 63965, 63966, 64100, 64101, 64102, 64103, 64104, 64105, 64106, 64107, 64108, 64109, 64110, 64246, 64247, 64248, 64249, 64250, 64251, 64252, 64253, 64254, 64255, 64256, 64396, 64397, 64398, 64399, 64400, 64401, 64402, 64403, 64404, 64405, 64406, 64538, 64539, 64540, 64541, 64542, 64543, 64544, 64545, 64546, 64547, 64683, 64684, 64685, 64686, 64687, 64688, 64689, 64690, 64691, 64692, 64693, 64830, 64831, 64832, 64833, 64834, 64835, 64836, 64837, 64838, 64839, 64973, 64974, 64975, 64976, 64977, 64978, 64979, 64980, 64981, 64982, 65116, 65117, 65118, 65119, 65120, 65121, 65122, 65123, 65124, 65125, 65265, 65266, 65267, 65268, 65269, 65270, 65271, 65272, 65273, 65405, 65406, 65407, 65408, 65409, 65410, 65411, 65412, 65413, 65414, 65548, 65549, 65550, 65551, 65552, 65553, 65554, 65555, 65556, 65689, 65690, 65691, 65692, 65693, 65694, 65695, 65696, 65697, 65831, 65832, 65833, 65834, 65835, 65836, 65837, 65838, 65839, 65977, 65978, 65979, 65980, 65981, 65982, 65983, 65984, 65985, 66126, 66127, 66128, 66129, 66130, 66131, 66132, 66133, 66134, 66269, 66270, 66271, 66272, 66273, 66274, 66275, 66276, 66277, 66414, 66415, 66416, 66417, 66418, 66419, 66420, 66421, 66422, 66551, 66552, 66553, 66554, 66555, 66556, 66557, 66558, 66559, 66678, 66679, 66680, 66681, 66682, 66683, 66684, 66685, 66686, 66790, 66791, 66792, 66793, 66794, 66795, 66796, 66797, 66891, 66892, 66893, 66894, 66895, 66896, 66897, 66898, 66899, 66968, 66969, 66970, 66971, 66972, 66973, 66974, 66975, 66976, 67084, 67085, 67086, 67087, 67088, 67089, 67090, 67091, 67092, 67186, 67187, 67188, 67189, 67190, 67191, 67192, 67193, 67194, 67321, 67322, 67323, 67324, 67325, 67326, 67327, 67328, 67329, 67414, 67415, 67416, 67417, 67418, 67419, 67420, 67421, 67422, 67512, 67513, 67514, 67515, 67516, 67517, 67518, 67519, 67520, 67608, 67609, 67610, 67611, 67612, 67613, 67614, 67615, 67616, 67689, 67690, 67691, 67692, 67693, 67694, 67695, 67696, 67697, 67773, 67774, 67775, 67776, 67777, 67778, 67779, 67780, 67781, 67856, 67857, 67858, 67859, 67860, 67861, 67862, 67863, 67864, 67936, 67937, 67938, 67939, 67940, 67941, 67942, 67943, 67944, 67945, 68014, 68015, 68016, 68017, 68018, 68019, 68020, 68021, 68022, 68090, 68092, 68093, 68094, 68095, 68096, 68097, 68098, 68099, 68100, 68163, 68164, 68165, 68166, 68167, 68168, 68169, 68170, 68171, 68172, 68229, 68230, 68231, 68232, 68233, 68234, 68235, 68236, 68237, 68297, 68298, 68299, 68300, 68301, 68302, 68303, 68304, 68305, 68306, 68361, 68362, 68363, 68364, 68365, 68366, 68367, 68368, 68369, 68370, 68422, 68424, 68425, 68426, 68427, 68428, 68429, 68430, 68431, 68432, 68433, 68482, 68483, 68484, 68485, 68486, 68487, 68488, 68489, 68490, 68491, 68553, 68554, 68555, 68556, 68557, 68558, 68559, 68560, 68561, 68562, 68563, 68612, 68613, 68614, 68615, 68616, 68617, 68618, 68619, 68620, 68621, 68622, 68680, 68682, 68683, 68684, 68685, 68686, 68687, 68688, 68689, 68690, 68691, 68692, 68743, 68744, 68745, 68746, 68747, 68748, 68749, 68750, 68751, 68752, 68753, 68754, 68798, 68800, 68801, 68802, 68803, 68804, 68805, 68806, 68807, 68808, 68809, 68810, 68811, 68850, 68851, 68852, 68853, 68854, 68855, 68856, 68857, 68858, 68859, 68860, 68861, 68898, 68899, 68900, 68901, 68902, 68903, 68904, 68905, 68906, 68907, 68908, 68909, 68910, 68945, 68946, 68947, 68948, 68949, 68950, 68951, 68952, 68953, 68954, 68955, 68956, 68993, 68994, 68995, 68996, 68997, 68998, 68999, 69000, 69001, 69002, 69003, 69004, 69045, 69046, 69047, 69048, 69049, 69050, 69051, 69052, 69053, 69054, 69089, 69090, 69091, 69092, 69093, 69094, 69095, 69096, 69097, 69098, 69133, 69134, 69135, 69136, 69137, 69138, 69139, 69140, 69180, 69181, 69182, 69183, 69184, 69185, 69227, 69228, 69229, 69230, 69276, 69866, 69867, 69868, 69958, 69959, 69960, 69961, 69962, 69963, 69964, 70065, 70066, 70067, 70068, 70069, 70070, 70071, 70072, 70073, 70159, 70160, 70161, 70162, 70163, 70164, 70165, 70166, 70167, 70168, 70269, 70270, 70271, 70272, 70273, 70274, 70275, 70276, 70277, 70278, 70279, 70280, 70389, 70390, 70391, 70392, 70393, 70394, 70395, 70396, 70397, 70398, 70399, 70400, 70401, 70503, 70504, 70505, 70506, 70507, 70508, 70509, 70510, 70511, 70512, 70513, 70514, 70515, 70516, 70625, 70626, 70627, 70628, 70629, 70630, 70631, 70632, 70633, 70634, 70635, 70636, 70637, 70638, 70750, 70751, 70752, 70753, 70754, 70755, 70756, 70757, 70758, 70759, 70760, 70761, 70762, 70763, 70883, 70884, 70885, 70886, 70887, 70888, 70889, 70890, 70891, 70892, 70893, 70894, 71013, 71014, 71015, 71016, 71017, 71018, 71019, 71020, 71021, 71022, 71023, 71024, 71025, 71150, 71151, 71152, 71153, 71154, 71155, 71156, 71157, 71158, 71159, 71160, 71161, 71162, 71284, 71285, 71286, 71287, 71288, 71289, 71290, 71291, 71292, 71293, 71294, 71295, 71296, 71424, 71425, 71426, 71427, 71428, 71429, 71430, 71431, 71432, 71433, 71434, 71565, 71566, 71567, 71568, 71569, 71570, 71571, 71572, 71573, 71574, 71575, 71711, 71712, 71713, 71714, 71715, 71716, 71717, 71718, 71719, 71720, 71721, 71859, 71860, 71861, 71862, 71863, 71864, 71865, 71866, 71867, 71868, 71999, 72000, 72001, 72002, 72003, 72004, 72005, 72006, 72007, 72008, 72009, 72150, 72151, 72152, 72153, 72154, 72155, 72156, 72157, 72158, 72159, 72293, 72294, 72295, 72296, 72297, 72298, 72299, 72300, 72301, 72302, 72432, 72433, 72434, 72435, 72436, 72437, 72438, 72439, 72440, 72441, 72575, 72576, 72577, 72578, 72579, 72580, 72581, 72582, 72583, 72716, 72717, 72718, 72719, 72720, 72721, 72722, 72723, 72724, 72725, 72862, 72863, 72864, 72865, 72866, 72867, 72868, 72869, 72870, 72871, 73005, 73006, 73007, 73008, 73009, 73010, 73011, 73012, 73013, 73145, 73146, 73147, 73148, 73149, 73150, 73151, 73152, 73153, 73288, 73289, 73290, 73291, 73292, 73293, 73294, 73295, 73296, 73297, 73432, 73433, 73434, 73435, 73436, 73437, 73438, 73439, 73440, 73576, 73577, 73578, 73579, 73580, 73581, 73582, 73583, 73584, 73714, 73715, 73716, 73717, 73718, 73719, 73720, 73721, 73722, 73850, 73851, 73852, 73853, 73854, 73855, 73856, 73857, 73858, 73978, 73979, 73980, 73981, 73982, 73983, 73984, 73985, 73986, 74094, 74095, 74096, 74097, 74098, 74099, 74100, 74101, 74102, 74207, 74208, 74209, 74210, 74211, 74212, 74213, 74214, 74215, 74286, 74287, 74288, 74289, 74290, 74291, 74292, 74293, 74294, 74408, 74409, 74410, 74411, 74412, 74413, 74414, 74415, 74416, 74533, 74534, 74535, 74536, 74537, 74538, 74539, 74540, 74541, 74662, 74663, 74664, 74665, 74666, 74667, 74668, 74669, 74670, 74755, 74756, 74757, 74758, 74759, 74760, 74761, 74762, 74763, 74846, 74847, 74848, 74849, 74850, 74851, 74852, 74853, 74854, 74940, 74941, 74942, 74943, 74944, 74945, 74946, 74947, 74948, 75030, 75031, 75032, 75033, 75034, 75035, 75036, 75037, 75038, 75111, 75112, 75113, 75114, 75115, 75116, 75117, 75118, 75119, 75193, 75194, 75195, 75196, 75197, 75198, 75199, 75200, 75201, 75276, 75277, 75278, 75279, 75280, 75281, 75282, 75283, 75284, 75285, 75359, 75360, 75361, 75362, 75363, 75364, 75365, 75366, 75367, 75438, 75439, 75440, 75441, 75442, 75443, 75444, 75445, 75446, 75447, 75514, 75515, 75516, 75517, 75518, 75519, 75520, 75521, 75522, 75523, 75582, 75583, 75584, 75585, 75586, 75587, 75588, 75589, 75590, 75591, 75647, 75648, 75649, 75650, 75651, 75652, 75653, 75654, 75655, 75656, 75719, 75720, 75721, 75722, 75723, 75724, 75725, 75726, 75727, 75728, 75781, 75782, 75783, 75784, 75785, 75786, 75787, 75788, 75789, 75790, 75791, 75844, 75845, 75846, 75847, 75848, 75849, 75850, 75851, 75852, 75853, 75854, 75905, 75906, 75907, 75908, 75909, 75910, 75911, 75912, 75913, 75914, 75915, 75975, 75976, 75977, 75978, 75979, 75980, 75981, 75982, 75983, 75984, 75985, 75986, 76035, 76036, 76037, 76038, 76039, 76040, 76041, 76042, 76043, 76044, 76045, 76046, 76097, 76098, 76099, 76100, 76101, 76102, 76103, 76104, 76105, 76106, 76107, 76108, 76109, 76158, 76159, 76160, 76161, 76162, 76163, 76164, 76165, 76166, 76167, 76168, 76169, 76170, 76171, 76213, 76214, 76215, 76216, 76217, 76218, 76219, 76220, 76221, 76222, 76223, 76224, 76266, 76267, 76268, 76269, 76270, 76271, 76272, 76273, 76274, 76275, 76276, 76277, 76278, 76321, 76322, 76323, 76324, 76325, 76326, 76327, 76328, 76329, 76330, 76331, 76332, 76333, 76334, 76378, 76379, 76380, 76381, 76382, 76383, 76384, 76385, 76386, 76387, 76388, 76389, 76390, 76430, 76431, 76432, 76433, 76434, 76435, 76436, 76437, 76438, 76439, 76440, 76488, 76489, 76490, 76491, 76492, 76493, 76494, 76495, 76496, 76539, 76540, 76541, 76542, 76543, 76544, 76545, 76546, 76589, 76590, 76591, 76592, 76593, 76594, 76639, 76640, 79193, 79194, 79195, 79196, 79197, 79198, 79199, 79200, 79307, 79308, 79309, 79310, 79311, 79312, 79313, 79314, 79315, 79429, 79430, 79431, 79432, 79433, 79434, 79847, 79848, 79849, 79850, 79851, 79971, 79973, 79974, 79975, 79976, 79977, 79978, 79979, 79980, 80109, 80110, 80111, 80112, 80113, 80114, 80115, 80116, 80247, 80248, 80249, 80250, 80251, 80252, 80253, 80254, 80396, 80397, 80398, 80399, 80400, 80401, 80402, 80403, 80545, 80546, 80547, 80548, 80549, 80550, 80551, 80552, 80553, 80696, 80698, 80699, 80700, 80701, 80702, 80703, 80704, 80705, 80853, 80854, 80855, 80856, 80857, 80858, 80859, 80860, 80861, 81010, 81011, 81012, 81013, 81014, 81015, 81016, 81017, 81162, 81164, 81165, 81166, 81167, 81168, 81169, 81170, 81171, 81311, 81312, 81313, 81314, 81315, 81316, 81317, 81318, 81464, 81465, 81466, 81467, 81468, 81469, 81470, 81471, 81609, 81610, 81611, 81612, 81613, 81614, 81615, 81616, 81760, 81761, 81762, 81763, 81764, 81765, 81766, 81767, 81899, 81900, 81901, 81902, 81903, 81904, 81905, 81906, 82042, 82043, 82044, 82045, 82046, 82047, 82048, 82049, 82185, 82186, 82187, 82188, 82189, 82190, 82191, 82192, 82329, 82330, 82331, 82332, 82333, 82334, 82335, 82336, 82468, 82469, 82470, 82471, 82472, 82473, 82474, 82607, 82608, 82609, 82610, 82611, 82612, 82613, 82614, 82747, 82748, 82749, 82750, 82751, 82752, 82753, 82870, 82871, 82872, 82873, 82874, 82875, 82999, 83000, 83001, 83002, 83003, 83004, 83126, 83127, 83128, 83129, 83130, 83131, 83256, 83257, 83258, 83259, 83260, 83392, 83393, 83394, 83395, 83396, 83521, 83522, 83523, 83524, 83643, 83644, 83645, 83766, 83767, 87058, 87059, 87060, 87061, 87062, 87063, 87064, 87065, 87169, 87170, 87171, 87172, 87173, 87174, 87175, 87176, 87280, 87281, 87282, 87283, 87284, 87697, 87698, 87699, 87700, 87701, 87702, 87816, 87817, 87818, 87819, 87820, 87821, 87822, 87823, 87824, 87950, 87951, 87952, 87953, 87954, 87955, 87956, 87957, 88093, 88094, 88095, 88096, 88097, 88098, 88099, 88100, 88245, 88246, 88247, 88248, 88249, 88250, 88251, 88252, 88395, 88396, 88397, 88398, 88399, 88400, 88401, 88402, 88546, 88547, 88548, 88549, 88550, 88551, 88552, 88553, 88554, 88707, 88708, 88709, 88710, 88711, 88712, 88713, 88714, 88715, 88863, 88864, 88865, 88866, 88867, 88868, 88869, 88870, 89019, 89020, 89021, 89022, 89023, 89024, 89025, 89026, 89027, 89172, 89173, 89174, 89175, 89176, 89177, 89178, 89179, 89323, 89324, 89325, 89326, 89327, 89328, 89329, 89330, 89331, 89468, 89469, 89470, 89471, 89472, 89473, 89474, 89475, 89613, 89614, 89615, 89616, 89617, 89618, 89619, 89620, 89758, 89759, 89760, 89761, 89762, 89763, 89764, 89765, 89903, 89904, 89905, 89906, 89907, 89908, 89909, 89910, 90045, 90046, 90047, 90048, 90049, 90050, 90051, 90052, 90186, 90187, 90188, 90189, 90190, 90191, 90192, 90334, 90335, 90336, 90337, 90338, 90339, 90340, 90472, 90473, 90474, 90475, 90476, 90477, 90478, 90610, 90611, 90612, 90613, 90614, 90615, 90616, 90739, 90740, 90741, 90742, 90743, 90744, 90871, 90872, 90873, 90874, 90875, 90995, 90996, 90997, 90998, 90999, 91127, 91128, 91129, 91130, 91131, 91132, 91266, 91267, 91268, 91269, 91395, 91396, 91397, 91519, 91520, 91521, 91522, 91646, 91647, 91769, 97012, 97075, 97138, 97201, 97262, 97323, 97385, 97447, 97510, 97573, 97634, 97695, 97758, 97821, 97883, 97945, 98007, 98069, 98132, 98195, 98258, 98321, 98383, 98445, 98508, 98571, 98633, 98695, 98758, 98821, 98880, 98881, 98940, 99002, 99003, 99065, 99066, 99126, 99127, 99187, 99188, 99250, 99251, 99313, 99314, 99375, 99376, 99437, 99438, 99500, 99501, 99563, 99564, 99626, 99627, 99689, 99690, 99752, 99753, 99815, 99816, 99877, 99878, 99939, 99940, 100002, 100003, 100065, 100066, 100126, 100127, 100187, 100188, 100250, 100251, 100313, 100314, 100375, 100376, 100437, 100438, 100500, 100501, 100563, 100564, 100626, 100627, 100689, 100690, 100752, 100753, 100815, 100816, 100878, 100879, 100941, 100942, 101005, 101006, 101069, 101070, 101133, 101134, 101197, 101198, 101261, 101262, 101325, 101326, 101389, 101390, 101453, 101454, 101515, 101516, 101577, 101578, 101639, 101640, 101641, 101702, 101703, 101704, 101766, 101767, 101768, 101830, 101831, 101832, 101894, 101895, 101896, 101958, 101959, 101960, 102022, 102023, 102024, 102086, 102087, 102088, 102150, 102151, 102152, 102214, 102215, 102216, 102277, 102278, 102279, 102340, 102341, 102342, 102404, 102405, 102406, 102468, 102469, 102470, 102531, 102532, 102533, 102594, 102595, 102596, 102657, 102658, 102659, 102720, 102721, 102722, 102781, 102782, 102783, 102842, 102843, 102844, 102906, 102907, 102908, 102970, 102971, 102972, 103034, 103035, 103036, 103098, 103099, 103100, 103162, 103163, 103164, 103226, 103227, 103228, 103290, 103291, 103292, 103354, 103355, 103356, 103417, 103418, 103419, 103480, 103481, 103482, 103544, 103545, 103546, 103608, 103609, 103610, 103671, 103672, 103673, 103734, 103735, 103736, 103798, 103799, 103800, 103862, 103863, 103864, 103926, 103927, 103928, 103990, 103991, 103992, 104054, 104055, 104118, 104119, 104182, 104183, 104246, 104247, 104309, 104310, 104372, 104373, 104435, 104436, 104437, 104498, 104499, 104500, 104562, 104563, 104564, 104626, 104627, 104628, 104688, 104689, 104690, 104750, 104751, 104752, 104813, 104814, 104815, 104876, 104877, 104878, 104938, 104939, 104940, 105000, 105001, 105002, 105062, 105063, 105064, 105124, 105125, 105126, 105185, 105186, 105187, 105188, 105247, 105248, 105249, 105250, 105309, 105310, 105311, 105371, 105372, 105373, 105433, 105434, 105435, 105495, 105496, 105497, 105558, 105559, 105560, 105621, 105622, 105623, 105684, 105685, 105686, 105747, 105748, 105749, 105810, 105811, 105812, 105873, 105874, 105875, 105936, 105937, 105938, 105999, 106000, 106001, 106062, 106063, 106064, 106125, 106126, 106127, 106187, 106188, 106189, 106249, 106250, 106251, 106312, 106313, 106314, 106375, 106376, 106377, 106438, 106439, 106440, 106501, 106502, 106503, 106564, 106565, 106566, 106627, 106628, 106629, 106690, 106691, 106692, 106753, 106754, 106755, 106817, 106818, 106819, 106881, 106882, 106883, 106944, 106945, 106946, 107007, 107008, 107009, 107069, 107070, 107071, 107131, 107132, 107133, 107195, 107196, 107197, 107259, 107260, 107261, 107323, 107324, 107325, 107387, 107388, 107389, 107450, 107451, 107452, 107513, 107514, 107515, 107576, 107577, 107578, 107639, 107640, 107641, 107703, 107704, 107705, 107767, 107768, 107769, 107831, 107832, 107833, 107895, 107896, 107897, 107959, 107960, 107961, 108023, 108024, 108025, 108087, 108088, 108089, 108151, 108152, 108153, 108215, 108216, 108217, 108279, 108280, 108281, 108343, 108344, 108345, 108407, 108408, 108409, 108470, 108471, 108472, 108473, 108534, 108535, 108536, 108537, 108598, 108599, 108600, 108601, 108662, 108663, 108664, 108665, 108726, 108727, 108728, 108729, 108790, 108791, 108792, 108793, 108854, 108855, 108856, 108857, 108918, 108919, 108920, 108921, 108982, 108983, 108984, 108985, 109046, 109047, 109048, 109049, 109109, 109110, 109111, 109112, 109172, 109173, 109174, 109175, 109235, 109236, 109237, 109238, 109298, 109299, 109300, 109301, 109360, 109361, 109362, 109363, 109422, 109423, 109424, 109425, 109485, 109486, 109487, 109488, 109548, 109549, 109550, 109551, 109612, 109613, 109614, 109615, 109676, 109677, 109678, 109679, 109740, 109741, 109742, 109743, 109804, 109805, 109806, 109807, 109867, 109868, 109869, 109870, 109930, 109931, 109932, 109933, 109994, 109995, 109996, 109997, 110058, 110059, 110060, 110061, 110122, 110123, 110124, 110125, 110186, 110187, 110188, 110189, 110250, 110251, 110252, 110253, 110314, 110315, 110316, 110317, 110378, 110379, 110380, 110381, 110442, 110443, 110444, 110445, 110506, 110507, 110508, 110509, 110570, 110571, 110572, 110573, 110634, 110635, 110636, 110637, 110698, 110699, 110700, 110701, 110762, 110763, 110764, 110765, 110826, 110827, 110828, 110829, 110889, 110890, 110891, 110892, 110952, 110953, 110954, 110955, 111016, 111017, 111018, 111019, 111080, 111081, 111082, 111083, 111143, 111144, 111145, 111146, 111206, 111207, 111208, 111209, 111270, 111271, 111272, 111273, 111334, 111335, 111336, 111337, 111397, 111398, 111399, 111400, 111459, 111460, 111461, 111462, 111463, 111523, 111524, 111525, 111526, 111527, 111587, 111588, 111589, 111590, 111591, 111651, 111652, 111653, 111654, 111655, 111715, 111716, 111717, 111718, 111719, 111779, 111780, 111781, 111782, 111783, 111843, 111844, 111845, 111846, 111847, 111906, 111907, 111908, 111909, 111910, 111969, 111970, 111971, 111972, 111973, 112033, 112034, 112035, 112036, 112037, 112097, 112098, 112099, 112100, 112101, 112160, 112161, 112162, 112163, 112164, 112223, 112224, 112225, 112226, 112227, 112287, 112288, 112289, 112290, 112291, 112351, 112352, 112353, 112354, 112355, 112415, 112416, 112417, 112418, 112419, 112479, 112480, 112481, 112482, 112483, 112542, 112543, 112544, 112545, 112546, 112605, 112606, 112607, 112608, 112609, 112669, 112670, 112671, 112672, 112673, 112733, 112734, 112735, 112736, 112737, 112796, 112797, 112798, 112799, 112800, 112859, 112860, 112861, 112862, 112863, 112922, 112923, 112924, 112925, 112926, 112985, 112986, 112987, 112988, 112989, 113048, 113049, 113050, 113051, 113052, 113111, 113112, 113113, 113114, 113115, 113175, 113176, 113177, 113178, 113179, 113239, 113240, 113241, 113242, 113243, 113303, 113304, 113305, 113306, 113307, 113367, 113368, 113369, 113370, 113371, 113430, 113431, 113432, 113433, 113434, 113493, 113494, 113495, 113496, 113497, 113556, 113557, 113558, 113559, 113560, 113619, 113620, 113621, 113622, 113623, 113682, 113683, 113684, 113685, 113686, 113745, 113746, 113747, 113748, 113749, 113807, 113808, 113809, 113810, 113811, 113869, 113870, 113871, 113872, 113873, 113933, 113934, 113935, 113936, 113937, 113997, 113998, 113999, 114000, 114001, 114060, 114061, 114062, 114063, 114064, 114123, 114124, 114125, 114126, 114127, 114187, 114188, 114189, 114190, 114191, 114251, 114252, 114253, 114254, 114255, 114315, 114316, 114317, 114318, 114319, 114378, 114379, 114380, 114381, 114382, 114383, 114441, 114442, 114443, 114444, 114445, 114446, 114504, 114505, 114506, 114507, 114508, 114509, 114566, 114567, 114568, 114569, 114570, 114571, 114628, 114629, 114630, 114631, 114632, 114633, 114692, 114693, 114694, 114695, 114696, 114697, 114756, 114757, 114758, 114759, 114760, 114761, 114820, 114821, 114822, 114823, 114824, 114825, 114884, 114885, 114886, 114887, 114888, 114889, 114948, 114949, 114950, 114951, 114952, 115012, 115013, 115014, 115015, 115016, 115076, 115077, 115078, 115079, 115080, 115140, 115141, 115142, 115143, 115144, 115203, 115204, 115205, 115206, 115207, 115266, 115267, 115268, 115269, 115270, 115330, 115331, 115332, 115333, 115334, 115394, 115395, 115396, 115397, 115398, 115458, 115459, 115460, 115461, 115462, 115522, 115523, 115524, 115525, 115526, 115584, 115585, 115586, 115587, 115588, 115646, 115647, 115648, 115649, 115650, 115710, 115711, 115712, 115713, 115714, 115774, 115775, 115776, 115777, 115778, 115837, 115838, 115839, 115840, 115841, 115900, 115901, 115902, 115903, 115904, 115964, 115965, 115966, 115967, 115968, 116028, 116029, 116030, 116031, 116032, 116092, 116093, 116094, 116095, 116096, 116156, 116157, 116158, 116159, 116160, 116220, 116221, 116222, 116223, 116224, 116284, 116285, 116286, 116287, 116288, 116348, 116349, 116350, 116351, 116352, 116412, 116413, 116414, 116415, 116416, 116476, 116477, 116478, 116479, 116540, 116541, 116542, 116543, 116603, 116604, 116605, 116606, 116666, 116667, 116668, 116669, 116730, 116731, 116732, 116733, 116794, 116795, 116796, 116797, 116856, 116857, 116858, 116859, 116918, 116919, 116920, 116921, 116981, 116982, 116983, 116984, 116985, 117045, 117046, 117047, 117048, 117049, 117109, 117110, 117111, 117112, 117113, 117173, 117174, 117175, 117176, 117177, 117237, 117238, 117239, 117240, 117241, 117301, 117302, 117303, 117304, 117305, 117365, 117366, 117367, 117368, 117369, 117429, 117430, 117431, 117432, 117433, 117492, 117493, 117494, 117495, 117496, 117555, 117556, 117557, 117558, 117559, 117618, 117619, 117620, 117621, 117622, 117681, 117682, 117683, 117684, 117685, 117744, 117745, 117746, 117747, 117807, 117808, 117809, 117810, 117871, 117872, 117873, 117874, 117935, 117936, 117937, 117938, 117999, 118000, 118001, 118002, 118063, 118064, 118065, 118066, 118127, 118128, 118129, 118130, 118191, 118192, 118193, 118194, 118254, 118255, 118256, 118257, 118317, 118318, 118319, 118320, 118381, 118382, 118383, 118384, 118445, 118446, 118447, 118448, 118508, 118509, 118510, 118511, 118571, 118572, 118573, 118574, 118635, 118636, 118637, 118638, 118699, 118700, 118701, 118702, 118762, 118763, 118764, 118765, 118825, 118826, 118827, 118828, 118889, 118890, 118891, 118892, 118953, 118954, 118955, 118956, 119016, 119017, 119018, 119019, 119079, 119080, 119081, 119082, 119142, 119143, 119144, 119145, 119205, 119206, 119207, 119208, 119269, 119270, 119271, 119272, 119333, 119334, 119335, 119336, 119397, 119398, 119399, 119400, 119456, 119460, 119461, 119462, 119463, 119464, 119525, 119526, 119527, 119528, 119584, 119588, 119589, 119590, 119591, 119592, 119652, 119653, 119654, 119655, 119656, 119712, 119716, 119717, 119718, 119719, 119720, 119775, 119779, 119780, 119781, 119782, 119838, 119842, 119843, 119844, 119845, 119902, 119906, 119907, 119908, 119909, 119966, 119970, 119971, 119972, 119973, 120030, 120034, 120035, 120036, 120037, 120094, 120098, 120099, 120100, 120101, 120158, 120162, 120163, 120164, 120165, 120222, 120226, 120227, 120228, 120229, 120285, 120289, 120290, 120291, 120292, 120348, 120352, 120353, 120354, 120355, 120412, 120416, 120417, 120418, 120419, 120476, 120480, 120481, 120482, 120483, 120537, 120541, 120542, 120543, 120544, 120598, 120602, 120603, 120604, 120605, 120662, 120666, 120667, 120668, 120669, 120726, 120730, 120731, 120732, 120733, 120789, 120793, 120794, 120795, 120796, 120852, 120856, 120857, 120858, 120859, 120915, 120919, 120920, 120921, 120922, 120978, 120982, 120983, 120984, 120985, 121042, 121046, 121047, 121048, 121049, 121105, 121106, 121110, 121111, 121112, 121113, 121170, 121174, 121175, 121176, 121177, 121233, 121234, 121238, 121239, 121240, 121241, 121295, 121296, 121300, 121301, 121302, 121303, 121357, 121358, 121362, 121363, 121364, 121365, 121420, 121421, 121425, 121426, 121427, 121483, 121484, 121488, 121489, 121490, 121546, 121547, 121551, 121552, 121553, 121609, 121610, 121614, 121615, 121616, 121673, 121674, 121678, 121679, 121680, 121737, 121738, 121742, 121743, 121744, 121800, 121801, 121805, 121806, 121807, 121858, 121863, 121864, 121868, 121869, 121870, 121926, 121927, 121931, 121932, 121933, 121984, 121989, 121990, 121993, 121994, 121995, 121996, 122053, 122054, 122058, 122059, 122060, 122112, 122117, 122118, 122121, 122122, 122123, 122124, 122176, 122181, 122182, 122185, 122186, 122187, 122188, 122240, 122245, 122246, 122249, 122250, 122251, 122252, 122304, 122309, 122310, 122313, 122314, 122315, 122316, 122368, 122373, 122374, 122377, 122378, 122379, 122380, 122432, 122437, 122438, 122441, 122442, 122443, 122444, 122496, 122501, 122502, 122505, 122506, 122507, 122508, 122559, 122564, 122565, 122568, 122569, 122570, 122571, 122622, 122627, 122628, 122631, 122632, 122633, 122634, 122682, 122687, 122688, 122691, 122692, 122693, 122742, 122746, 122747, 122748, 122751, 122752, 122753, 122806, 122811, 122812, 122815, 122816, 122817, 122870, 122874, 122875, 122876, 122879, 122880, 122881, 122933, 122937, 122938, 122939, 122942, 122943, 122944, 122996, 123000, 123001, 123002, 123005, 123006, 123007, 123059, 123063, 123064, 123065, 123068, 123069, 123070, 123122, 123126, 123127, 123128, 123131, 123132, 123133, 123186, 123190, 123191, 123192, 123195, 123196, 123197, 123250, 123254, 123255, 123256, 123259, 123260, 123261, 123312, 123316, 123317, 123318, 123321, 123322, 123323, 123374, 123378, 123379, 123380, 123383, 123384, 123385, 123436, 123440, 123441, 123442, 123445, 123446, 123447, 123497, 123498, 123502, 123503, 123504, 123507, 123508, 123509, 123561, 123565, 123566, 123567, 123570, 123571, 123572, 123623, 123624, 123628, 123629, 123630, 123632, 123633, 123634, 123635, 123686, 123687, 123691, 123692, 123693, 123696, 123697, 123698, 123749, 123750, 123754, 123755, 123756, 123758, 123759, 123760, 123761, 123810, 123811, 123815, 123816, 123817, 123819, 123820, 123821, 123822, 123871, 123872, 123876, 123877, 123878, 123880, 123881, 123882, 123883, 123933, 123934, 123938, 123939, 123942, 123943, 123944, 123945, 123995, 123996, 124000, 124001, 124004, 124005, 124006, 124007, 124056, 124057, 124061, 124062, 124065, 124066, 124067, 124117, 124118, 124122, 124123, 124126, 124127, 124128, 124178, 124179, 124183, 124184, 124187, 124188, 124189, 124234, 124239, 124240, 124244, 124245, 124248, 124249, 124250, 124302, 124303, 124307, 124308, 124311, 124312, 124313, 124360, 124365, 124366, 124369, 124370, 124371, 124374, 124375, 124376, 124422, 124427, 124428, 124431, 124432, 124435, 124436, 124437, 124483, 124488, 124489, 124491, 124492, 124493, 124496, 124497, 124498, 124545, 124550, 124551, 124555, 124556, 124559, 124560, 124561, 124608, 124613, 124614, 124617, 124618, 124619, 124622, 124623, 124624, 124670, 124675, 124676, 124679, 124680, 124681, 124684, 124685, 124686, 124732, 124737, 124738, 124741, 124742, 124743, 124746, 124747, 124748, 124794, 124799, 124800, 124803, 124804, 124805, 124808, 124809, 124810, 124856, 124861, 124862, 124865, 124866, 124867, 124870, 124871, 124872, 124918, 124923, 124924, 124927, 124928, 124929, 124932, 124933, 124934, 124980, 124985, 124986, 124989, 124990, 124991, 124994, 124995, 124996, 125043, 125048, 125049, 125052, 125053, 125054, 125057, 125058, 125059, 125106, 125110, 125111, 125112, 125115, 125116, 125117, 125120, 125121, 125122, 125169, 125174, 125175, 125178, 125179, 125180, 125183, 125184, 125185, 125232, 125236, 125237, 125238, 125241, 125242, 125243, 125245, 125246, 125247, 125248, 125290, 125295, 125299, 125300, 125301, 125304, 125305, 125306, 125348, 125352, 125353, 125357, 125358, 125359, 125361, 125362, 125363, 125364, 125409, 125413, 125414, 125418, 125419, 125422, 125423, 125424, 125425, 125470, 125474, 125475, 125479, 125480, 125483, 125484, 125485, 125486, 125533, 125537, 125538, 125542, 125543, 125546, 125547, 125548, 125596, 125600, 125601, 125605, 125606, 125609, 125610, 125611, 125659, 125663, 125664, 125668, 125669, 125672, 125673, 125674, 125722, 125726, 125727, 125731, 125732, 125735, 125736, 125737, 125782, 125786, 125787, 125791, 125792, 125795, 125796, 125797, 125841, 125842, 125846, 125847, 125851, 125852, 125855, 125856, 125857, 125901, 125905, 125906, 125910, 125911, 125914, 125915, 125916, 125959, 125960, 125964, 125965, 125968, 125969, 125970, 125973, 125974, 125975, 126021, 126022, 126026, 126027, 126031, 126032, 126035, 126036, 126037, 126083, 126084, 126088, 126089, 126092, 126093, 126094, 126097, 126098, 126099, 126143, 126144, 126148, 126149, 126152, 126153, 126154, 126157, 126158, 126159, 126203, 126204, 126208, 126209, 126212, 126213, 126214, 126217, 126218, 126219, 126264, 126265, 126269, 126270, 126272, 126273, 126274, 126277, 126278, 126279, 126324, 126325, 126329, 126330, 126332, 126333, 126334, 126337, 126338, 126339, 126384, 126385, 126389, 126390, 126393, 126394, 126395, 126398, 126399, 126400, 126445, 126446, 126450, 126451, 126454, 126455, 126456, 126459, 126460, 126461, 126504, 126505, 126509, 126510, 126513, 126514, 126515, 126518, 126519, 126520, 126558, 126563, 126564, 126568, 126569, 126572, 126573, 126574, 126577, 126578, 126579, 126617, 126622, 126623, 126627, 126628, 126631, 126632, 126633, 126636, 126637, 126638, 126676, 126681, 126682, 126685, 126686, 126687, 126690, 126691, 126692, 126695, 126696, 126697, 126736, 126741, 126746, 126747, 126750, 126751, 126752, 126755, 126756, 126757, 126796, 126801, 126805, 126806, 126807, 126810, 126811, 126812, 126815, 126816, 126817, 126858, 126863, 126868, 126872, 126873, 126877, 126878, 126879, 126920, 126925, 126929, 126930, 126934, 126935, 126939, 126940, 126941, 126982, 126987, 126991, 126992, 126996, 126997, 127000, 127001, 127043, 127048, 127052, 127053, 127057, 127058, 127061, 127062, 127101, 127106, 127110, 127111, 127115, 127116, 127120, 127121, 127160, 127165, 127169, 127170, 127174, 127175, 127179, 127180, 127218, 127223, 127227, 127228, 127232, 127233, 127237, 127238, 127276, 127281, 127285, 127286, 127290, 127291, 127295, 127296, 127336, 127340, 127341, 127345, 127346, 127350, 127351, 127390, 127391, 127395, 127396, 127400, 127401, 127405, 127406, 127442, 127447, 127451, 127452, 127456, 127457, 127461, 127462, 127498, 127502, 127503, 127507, 127508, 127511, 127512, 127513, 127517, 127518, 127552, 127556, 127557, 127561, 127562, 127565, 127566, 127570, 127571, 127604, 127608, 127609, 127613, 127614, 127616, 127617, 127618, 127622, 127623, 127657, 127661, 127662, 127665, 127666, 127669, 127670, 127671, 127675, 127676, 127711, 127715, 127716, 127719, 127720, 127723, 127724, 127725, 127729, 127730, 127762, 127765, 127766, 127770, 127771, 127774, 127775, 127776, 127780, 127781, 127813, 127816, 127817, 127821, 127822, 127825, 127826, 127827, 127831, 127832, 127866, 127870, 127871, 127875, 127876, 127879, 127880, 127881, 127885, 127886, 127920, 127924, 127925, 127929, 127930, 127933, 127934, 127935, 127939, 127940, 127979, 127980, 127984, 127985, 127988, 127989, 127990, 127994, 127995, 128034, 128035, 128039, 128040, 128043, 128044, 128045, 128049, 128050, 128082, 128091, 128095, 128096, 128097, 128101, 128102, 128134, 128142, 128143, 128147, 128148, 128149, 128153, 128154, 128188, 128192, 128197, 128201, 128202, 128206, 128207, 128241, 128245, 128249, 128250, 128254, 128255, 128259, 128260, 128292, 128297, 128301, 128302, 128306, 128307, 128312, 128345, 128350, 128354, 128355, 128359, 128360, 128365, 128396, 128399, 128403, 128404, 128408, 128409, 128414, 128445, 128448, 128452, 128453, 128457, 128458, 128463, 128500, 128504, 128505, 128509, 128510, 128515, 128552, 128556, 128557, 128561, 128562, 128567, 128603, 128607, 128608, 128612, 128613, 128618, 128654, 128658, 128659, 128663, 128664, 128669, 128706, 128710, 128711, 128715, 128716, 128721, 128758, 128762, 128763, 128767, 128768, 128773, 128807, 128812, 128816, 128817, 128821, 128822, 128827, 128861, 128865, 128866, 128870, 128871, 128875, 128876, 128881, 128916, 128920, 128921, 128925, 128926, 128930, 128931, 128936, 128971, 128975, 128976, 128980, 128981, 128985, 128986, 128991, 129028, 129029, 129033, 129034, 129038, 129039, 129044, 129081, 129082, 129086, 129087, 129091, 129092, 129097, 129132, 129133, 129137, 129138, 129142, 129143, 129148, 129183, 129184, 129188, 129189, 129193, 129194, 129199, 129234, 129235, 129239, 129240, 129244, 129245, 129250, 129285, 129286, 129290, 129291, 129295, 129296, 129301, 129335, 129340, 129341, 129345, 129346, 129351, 129385, 129390, 129391, 129395, 129396, 129401, 129434, 129439, 129444, 129483, 129488, 129493, 129530, 129534, 129538, 129543, 129580, 129584, 129587, 129588, 129593, 129629, 129634, 129639, 129675, 129679, 129680, 129685, 129725, 129729, 129730, 129735, 129775, 129779, 129780, 129785, 129819, 129824, 129828, 129829, 129834, 129868, 129873, 129877, 129878, 129883, 129922, 129926, 129927, 129932, 129970, 129974, 129975, 129980, 130017, 130021, 130022, 130026, 130064, 130068, 130069, 130073, 130109, 130113, 130114, 130119, 130154, 130155, 130159, 130160, 130165, 130199, 130200, 130204, 130205, 130210, 130244, 130245, 130249, 130250, 130255, 130291, 130292, 130296, 130297, 130302, 130338, 130339, 130343, 130344, 130349, 130382, 130383, 130386, 130387, 130392, 130425, 130426, 130429, 130430, 130435, 130465, 130470, 130506, 130511, 130549, 130554, 130559, 130597, 130602, 130607, 130644, 130648, 130652, 130689, 130693, 130697, 130733, 130738, 130743, 130779, 130784, 130789, 130827, 130831, 130869, 130873, 130908, 130913, 130949, 130954, 130989, 130993, 131030, 131034, 131070, 131073, 131078, 131114, 131117, 131122, 131159, 131164, 131201, 131202, 131207, 131240, 131243, 131244, 131249, 131282, 131285, 131286, 131291, 131330, 131331, 131336, 131374, 131375, 131380, 131417, 131423, 131461, 131467, 131506, 131551, 131595, 131638, 131681, 131725, 131770, 131815, 131854, 131859, 131900, 131905, 131945, 131950, 131990, 131995, 132039, 132083, 132126, 132170, 132215, 132260, 132301, 132306, 132348, 132353, 132398, 132444, 133394, 133448, 184809, 184814, 184820, 184824, 184828, 184829, 184832, 184837, 184838, 184842, 184843, 184848, 184849, 184850, 184854, 184855, 184860, 184861, 184862, 184866, 184867, 184872, 184873, 184874, 184878, 184879, 184880, 184886, 184887, 184888, 184892, 184893, 184894, 184900, 184901, 184902, 184906, 184907, 184908, 184915, 184916, 184917, 184922, 184923, 184924, 184932, 184933, 184934, 184939, 184940, 184941, 184949, 184950, 184951, 184958, 184959, 184960, 184968, 184969, 184970, 184977, 184978, 184979, 184988, 184989, 184990, 184991, 184998, 184999, 185000, 185009, 185010, 185011, 185012, 185020, 185021, 185022, 185032, 185033, 185034, 185035, 185043, 185044, 185045, 185056, 185057, 185058, 185059, 185069, 185070, 185071, 185072, 185085, 185086, 185087, 185088, 185099, 185100, 185101, 185102, 185118, 185119, 185120, 185121, 185135, 185136, 185137, 185138, 185154, 185155, 185156, 185157, 185173, 185174, 185175, 185176, 185194, 185195, 185196, 185213, 185214, 185215, 185233, 185234, 185235, 185252, 185253, 185254, 185272, 185273, 185274, 185292, 185293, 185294, 185312, 185313, 185314, 185332, 185333, 185334, 185352, 185353, 185354, 185372, 185373, 185374, 185393, 185394, 185395, 185413, 185414, 185415, 185436, 185437, 185438, 185456, 185457, 185458, 185480, 185481, 185482, 185502, 185503, 185504, 185529, 185530, 185531, 185553, 185554, 185555, 185581, 185582, 185583, 185607, 185608, 185609, 185635, 185636, 185637, 185638, 185663, 185664, 185665, 185692, 185693, 185694, 185695, 185720, 185721, 185722, 185749, 185750, 185751, 185752, 185778, 185779, 185780, 185807, 185808, 185809, 185810, 185836, 185837, 185838, 185839, 185866, 185867, 185868, 185869, 185895, 185896, 185897, 185898, 185925, 185926, 185927, 185928, 185954, 185955, 185956, 185957, 185986, 185987, 185988, 185989, 186015, 186016, 186017, 186018, 186048, 186049, 186050, 186051, 186078, 186079, 186080, 186081, 186115, 186116, 186117, 186118, 186147, 186148, 186149, 186150, 186185, 186186, 186187, 186188, 186219, 186220, 186221, 186222, 186258, 186259, 186260, 186261, 186296, 186297, 186298, 186299, 186335, 186336, 186337, 186338, 186372, 186373, 186374, 186375, 186413, 186414, 186415, 186416, 186452, 186453, 186454, 186455, 186494, 186495, 186496, 186534, 186535, 186536, 186577, 186578, 186579, 186618, 186619, 186620, 186660, 186661, 186662, 186701, 186702, 186703, 187946, 187947, 187948, 188052, 188053, 188054, 188157, 188158, 188159, 188212, 188213, 188214, 188268, 188269, 188270, 188324, 188325, 188326, 188378, 188379, 188380, 188432, 188433, 188434, 188487, 188488, 188489, 188542, 188543, 188544, 188598, 188599, 188600, 188654, 188655, 188656, 188711, 188712, 188713, 188767, 188768, 188769, 188826, 188827, 188882, 188883, 188943, 188944, 189002, 189003, 189063, 189064, 189122, 189123, 189183, 189184, 189244, 189245, 189305, 189306, 189366, 189367, 189427, 189428, 189488, 189489, 189549, 189550, 189610, 189611, 189671, 189672, 189732, 189733, 189793, 189794, 189854, 189855, 189915, 189916, 189976, 189977, 190036, 190037, 190096, 190097, 190156, 190157, 190216, 190217, 190277, 190278, 190338, 190339, 190397, 190398, 190456, 190457, 190517, 190518, 190578, 190579, 190638, 190639, 190698, 190699, 190758, 190759, 190818, 190819, 190878, 190879, 190938, 190939, 190998, 190999, 191058, 191059, 191118, 191119, 191178, 191179, 191238, 191239, 191298, 191299, 191359, 191360, 191420, 191421, 191481, 191482, 191542, 191543, 191601, 191602, 191660, 191661, 191719, 191720, 191778, 191779, 191838, 191839, 191898, 191899, 191958, 191959, 192018, 192019, 192078, 192079, 192138, 192139, 192196, 192197, 192254, 192255, 192315, 192375, 192435, 192495, 192555, 192615, 192674, 192733, 192793, 192853, 192913, 192973, 195149, 195267, 195386, 195447, 195507, 195567, 195627, 195687, 195748, 195809, 195869, 195929, 195990, 196051, 196112, 196173, 196234, 196295], "numberOfPointsInBox": 7455}, "attributes": {"trackId": "left_first_track"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "42116b2f-48f4-423e-9219-a8c6961ede1a", "objectId": "03a3625e-9082-41ec-8b44-24e92b7286eb", "className": "3D_track", "geometry": {"associatedPoints": [2699, 2713, 2719, 2740, 2758, 2781, 2786, 2791, 2814, 2819, 2825, 2841, 2847, 2853, 5805, 5810, 5814, 13349, 13374, 13401, 16271, 16277, 16283, 16294, 16300, 16306, 16312, 16333, 16339, 16349, 16355, 16361, 16367, 16398, 16413, 16418, 17590, 17596, 17602, 17608, 17609, 17615, 17621, 17622, 17627, 17628, 17634, 17640, 17641, 17647, 17653, 17654, 17659, 17660, 17666, 17672, 17673, 17679, 17685, 17691, 18423, 18429, 18439, 18444, 18449, 18464, 18475, 18480, 18486, 18490, 18496, 18502, 18512, 18524, 18530, 19052, 19058, 19064, 19070, 19076, 19077, 19082, 19083, 19088, 19089, 19094, 19095, 19100, 19101, 19106, 19107, 19108, 19112, 19113, 19114, 19119, 19120, 19125, 19126, 19131, 19132, 19133, 19137, 19138, 19139, 19144, 19145, 19150, 19151, 19156, 19157, 19162, 19163, 19164, 19170, 19176, 19182, 19188, 19189, 19195, 19201, 19207, 19213, 19219, 19225, 19543, 19549, 19555, 19561, 19566, 19567, 19572, 19573, 19578, 19579, 19584, 19590, 19595, 19596, 19601, 19602, 19607, 19613, 19618, 19619, 19624, 19625, 19630, 19636, 19641, 19642, 19647, 19648, 19653, 19659, 19664, 19665, 19670, 19676, 19682, 22024, 22030, 22043, 22049, 22062, 22101, 22857, 22876, 22886, 22895, 22900, 23323, 23329, 23335, 23341, 23347, 23353, 23354, 23359, 23360, 23365, 23366, 23371, 23372, 23378, 23379, 23384, 23385, 23390, 23391, 23396, 23397, 23402, 23403, 23404, 23409, 23410, 23415, 23416, 23421, 23422, 23427, 23428, 23429, 23434, 23435, 23440, 23441, 23446, 23447, 23452, 23453, 23454, 23459, 23460, 23465, 23466, 23471, 23472, 23477, 23478, 23484, 23490, 23496, 23502, 24000, 24006, 24012, 24018, 24023, 24024, 24029, 24035, 24040, 24041, 24046, 24052, 24057, 24058, 24063, 24069, 24074, 24075, 24080, 24086, 24091, 24092, 24097, 24103, 24109, 26456, 26474, 26475, 26481, 26487, 26488, 26494, 26500, 26506, 26507, 26513, 26519, 26520, 26526, 26532, 26538, 26539, 26545, 26551, 26557, 27628, 27634, 27640, 27641, 27646, 27647, 27652, 27653, 27658, 27659, 27665, 27666, 27671, 27672, 27677, 27678, 27683, 27684, 27689, 27690, 27691, 27696, 27697, 27702, 27703, 27708, 27709, 27715, 27716, 27721, 27722, 27727, 27728, 27733, 27734, 27735, 27740, 27741, 27746, 27747, 27752, 27753, 27759, 27765, 27771, 27777, 28442, 28459, 28476, 28504, 30862, 30868, 30874, 30880, 30881, 30887, 30893, 30899, 30900, 30906, 30912, 30918, 30919, 30925, 30931, 30937, 30938, 30944, 30950, 30956, 30969, 30975, 30981, 30987, 31826, 31832, 31838, 31844, 31845, 31850, 31851, 32671, 32677, 32683, 32688, 32694, 32700, 32705, 32711, 32717, 32722, 32728, 32733, 32734, 32739, 32745, 32750, 32751, 32756, 32762, 32768, 35138, 35144, 35150, 35156, 35162, 35163, 35169, 35175, 35181, 35187, 35188, 35200, 35206, 35212, 35217, 35223, 35228, 35258, 35264, 35994, 36000, 36006, 36012, 36013, 36018, 36019, 36025, 36031, 36032, 36037, 36038, 36044, 36050, 36051, 36056, 36057, 36063, 36069, 36070, 36075, 36076, 36082, 36083, 36088, 36089, 36094, 36095, 36101, 36107, 36856, 36862, 36867, 36868, 36873, 36879, 36884, 36885, 36890, 36896, 36901, 36902, 36907, 36913, 36918, 36919, 36924, 36930, 36935, 36936, 36941, 36947, 36953, 37697, 37703, 37709, 37715, 37721, 37727, 37733, 37734, 37740, 37746, 37752, 37758, 37764, 37769, 37770, 37776, 37777, 37782, 37783, 37788, 37789, 37794, 37795, 37800, 37801, 37806, 37807, 37812, 37813, 37818, 37819, 37824, 37825, 37830, 37831, 37836, 37837, 37842, 37843, 37848, 37849, 37854, 37855, 37856, 37861, 37862, 37867, 37868, 37873, 37874, 37879, 37880, 37885, 37886, 37891, 37892, 37897, 37898, 37902, 37903, 37904, 37909, 37914, 37915, 37920, 37921, 37926, 37927, 37932, 37933, 37938, 37939, 38003, 38004, 38009, 38010, 38015, 38016, 38021, 38022, 38027, 38033, 38039, 38045, 38051, 39377, 39383, 39389, 39395, 39401, 63381, 63382, 63515, 63516, 63653, 63791, 63792, 63937, 63938, 63939, 64083, 64084, 64085, 64228, 64229, 64230, 64231, 64377, 64378, 64379, 64518, 64519, 64520, 64521, 64662, 64664, 64665, 64666, 64808, 64809, 64810, 64811, 64812, 64953, 64954, 64955, 64956, 64957, 65096, 65097, 65098, 65099, 65100, 65245, 65246, 65247, 65248, 65386, 65387, 65388, 65389, 65528, 65529, 65530, 65531, 65669, 65670, 65671, 65672, 65673, 65811, 65812, 65813, 65814, 65815, 65816, 65957, 65958, 65959, 65960, 66106, 66107, 66108, 66109, 66110, 66247, 66249, 66250, 66251, 66252, 66253, 66394, 66395, 66396, 66397, 66398, 66532, 66533, 66534, 66535, 66536, 66659, 66660, 66661, 66662, 66770, 66771, 66772, 66773, 66774, 66872, 66873, 66874, 66875, 66946, 66947, 66948, 66949, 66950, 67062, 67063, 67065, 67067, 67165, 67166, 67167, 67168, 67170, 67301, 67302, 67303, 67304, 67305, 67393, 67395, 67396, 67397, 67398, 67492, 67493, 67494, 67495, 67496, 67588, 67589, 67590, 67591, 67592, 67670, 67671, 67672, 67673, 67674, 67753, 67754, 67755, 67756, 67757, 67835, 67837, 67838, 67839, 67840, 67917, 67918, 67919, 67920, 67921, 67995, 67996, 67997, 67998, 68072, 68073, 68074, 68075, 68145, 68146, 68147, 68148, 68210, 68211, 68212, 68213, 68279, 68280, 68281, 68282, 68344, 68345, 68346, 68405, 68406, 68407, 68408, 68465, 68466, 68467, 68536, 68537, 68538, 68539, 68596, 68597, 68598, 68665, 68666, 68667, 68729, 68730, 68785, 68786, 68787, 68838, 68839, 68888, 68936, 70778, 70779, 70907, 70908, 71038, 71039, 71040, 71177, 71178, 71311, 71312, 71313, 71449, 71450, 71451, 71590, 71591, 71592, 71593, 71737, 71738, 71739, 71740, 71886, 71887, 71888, 71889, 72028, 72029, 72030, 72031, 72177, 72178, 72179, 72180, 72318, 72319, 72320, 72321, 72322, 72457, 72458, 72459, 72460, 72600, 72601, 72602, 72603, 72741, 72742, 72743, 72744, 72745, 72888, 72889, 72890, 72891, 72892, 73030, 73031, 73032, 73033, 73169, 73170, 73171, 73172, 73173, 73313, 73314, 73315, 73316, 73317, 73318, 73457, 73458, 73459, 73460, 73461, 73600, 73601, 73602, 73603, 73604, 73738, 73739, 73740, 73741, 73742, 73874, 73875, 73876, 73877, 73878, 74002, 74003, 74004, 74005, 74006, 74118, 74119, 74120, 74121, 74122, 74231, 74232, 74233, 74234, 74235, 74311, 74312, 74313, 74314, 74315, 74438, 74439, 74440, 74441, 74557, 74558, 74559, 74560, 74561, 74686, 74687, 74688, 74689, 74690, 74779, 74780, 74781, 74782, 74783, 74870, 74871, 74872, 74873, 74874, 74964, 74965, 74966, 74967, 74968, 75053, 75054, 75055, 75056, 75057, 75135, 75136, 75137, 75138, 75217, 75218, 75219, 75220, 75221, 75301, 75302, 75303, 75304, 75383, 75384, 75385, 75386, 75387, 75462, 75463, 75464, 75465, 75539, 75540, 75541, 75542, 75606, 75607, 75608, 75609, 75672, 75673, 75674, 75675, 75743, 75744, 75745, 75746, 75806, 75807, 75808, 75868, 75869, 75870, 75871, 75929, 75930, 75931, 76000, 76001, 76002, 76059, 76060, 76061, 76122, 76123, 76124, 76183, 76184, 76236, 79181, 79182, 79296, 79297, 79298, 79418, 79419, 79420, 79421, 79842, 79961, 79962, 79963, 80095, 80097, 80098, 80099, 80234, 80235, 80236, 80237, 80382, 80384, 80385, 80532, 80533, 80534, 80684, 80685, 80686, 80841, 80842, 80843, 80997, 80998, 80999, 81000, 81151, 81152, 81298, 81300, 81301, 81452, 81453, 81454, 81598, 81599, 81600, 81748, 81749, 81750, 81751, 81888, 81889, 82030, 82032, 82033, 82175, 82176, 82177, 82320, 82321, 82459, 82460, 82598, 82599, 82739, 82740, 82861, 82862, 87076, 87077, 87078, 87187, 87188, 87189, 87292, 87293, 87294, 87588, 87833, 87834, 87835, 87836, 87968, 87969, 87970, 87971, 88110, 88111, 88112, 88113, 88263, 88264, 88265, 88413, 88414, 88415, 88565, 88566, 88567, 88726, 88727, 88728, 88880, 88881, 88882, 89037, 89038, 89039, 89040, 89189, 89190, 89341, 89342, 89343, 89485, 89486, 89487, 89630, 89631, 89632, 89774, 89775, 89919, 89920, 89921, 90061, 90062, 90201, 90348, 90349, 90485, 90486, 90623, 90624, 90751, 90752, 93118, 93142, 93164, 93165, 93188, 93189, 93218, 93219, 93285, 93286, 93322, 93354, 93355, 93389, 93423, 93424, 93459, 93494, 93495, 93532, 93572, 93573, 93616, 93617, 93654, 93655, 93697, 93698, 93731, 93732, 93768, 93769, 93807, 93808, 93848, 93849, 93889, 93890, 93933, 93934, 93979, 93980, 94027, 94028, 94072, 94073, 94120, 94121, 94167, 94168, 94215, 94216, 94267, 94320, 94370, 94421, 94472, 94524, 94576, 94577, 94629, 94630, 94686, 94687, 94741, 94742, 94799, 94800, 94856, 94857, 94916, 94917, 94976, 94977, 95037, 95038, 95098, 95099, 95157, 95158, 95216, 95217, 95277, 95278, 95339, 95340, 95400, 95401, 95461, 95462, 95523, 95585, 95647, 95648, 95709, 95710, 95771, 95772, 95833, 95834, 95895, 95956, 95957, 96017, 96076, 96077, 96137, 96196, 96197, 96259, 96320, 96321, 96383, 96445, 96446, 96509, 96571, 96572, 96635, 96697, 96698, 96760, 96820, 96821, 96882, 96942, 96943, 97005, 97067, 97068, 97131, 97193, 97194, 97255, 97315, 97316, 97377, 97378, 97439, 97440, 97502, 97503, 97565, 97566, 97627, 97687, 97688, 97750, 97751, 97813, 97814, 97875, 97876, 97937, 97938, 97999, 98000, 98061, 98062, 98124, 98125, 98187, 98188, 98250, 98251, 98313, 98314, 98375, 98376, 98437, 98438, 98500, 98501, 98560, 98563, 98564, 98625, 98626, 98684, 98687, 98688, 98750, 98751, 98810, 98813, 98814, 98872, 98873, 98929, 98931, 98932, 98933, 98994, 98995, 99054, 99056, 99057, 99058, 99118, 99119, 99176, 99178, 99179, 99180, 99242, 99243, 99302, 99304, 99305, 99306, 99367, 99368, 99427, 99428, 99429, 99430, 99492, 99493, 99552, 99554, 99555, 99556, 99618, 99619, 99678, 99680, 99681, 99682, 99744, 99745, 99804, 99806, 99807, 99808, 99869, 99870, 99928, 99930, 99931, 99932, 99991, 99994, 99995, 100054, 100056, 100057, 100058, 100115, 100118, 100119, 100176, 100178, 100179, 100180, 100239, 100242, 100243, 100302, 100304, 100305, 100306, 100364, 100367, 100368, 100426, 100428, 100429, 100430, 100489, 100491, 100492, 100493, 100552, 100554, 100555, 100556, 100615, 100617, 100618, 100619, 100678, 100680, 100681, 100682, 100741, 100743, 100744, 100745, 100804, 100806, 100807, 100808, 100868, 100869, 100870, 100871, 100931, 100932, 100933, 100934, 100994, 100996, 100997, 100998, 101057, 101058, 101060, 101061, 101062, 101122, 101124, 101125, 101126, 101185, 101186, 101188, 101189, 101190, 101250, 101252, 101253, 101254, 101313, 101314, 101316, 101317, 101318, 101378, 101380, 101381, 101382, 101441, 101442, 101444, 101445, 101446, 101505, 101507, 101508, 101509, 101566, 101567, 101569, 101570, 101571, 101629, 101631, 101632, 101633, 101691, 101692, 101694, 101695, 101696, 101756, 101758, 101759, 101760, 101819, 101820, 101821, 101822, 101823, 101824, 101884, 101886, 101887, 101888, 101947, 101948, 101949, 101950, 101951, 101952, 102012, 102014, 102015, 102016, 102075, 102076, 102077, 102078, 102079, 102080, 102140, 102142, 102143, 102144, 102203, 102204, 102205, 102206, 102207, 102208, 102267, 102269, 102270, 102271, 102329, 102330, 102331, 102332, 102333, 102334, 102393, 102394, 102396, 102397, 102398, 102457, 102458, 102459, 102460, 102461, 102462, 102520, 102521, 102523, 102524, 102525, 102583, 102584, 102585, 102586, 102587, 102588, 102646, 102647, 102649, 102650, 102651, 102709, 102710, 102711, 102712, 102713, 102714, 102770, 102771, 102773, 102774, 102775, 102831, 102832, 102833, 102834, 102835, 102836, 102895, 102896, 102898, 102899, 102900, 102959, 102960, 102961, 102962, 102963, 102964, 103023, 103024, 103026, 103027, 103028, 103087, 103088, 103089, 103090, 103091, 103092, 103151, 103152, 103153, 103154, 103155, 103156, 103215, 103216, 103217, 103218, 103219, 103220, 103279, 103280, 103281, 103282, 103283, 103284, 103343, 103344, 103345, 103346, 103347, 103348, 103406, 103407, 103408, 103409, 103410, 103411, 103469, 103470, 103471, 103472, 103473, 103474, 103533, 103534, 103535, 103536, 103537, 103597, 103598, 103599, 103600, 103601, 103660, 103661, 103662, 103663, 103664, 103665, 103722, 103723, 103724, 103725, 103726, 103727, 103728, 103787, 103788, 103789, 103790, 103791, 103792, 103850, 103851, 103852, 103853, 103854, 103855, 103856, 103915, 103916, 103917, 103918, 103919, 103920, 103978, 103979, 103980, 103981, 103982, 103983, 103984, 104043, 104044, 104045, 104046, 104047, 104106, 104107, 104108, 104109, 104110, 104111, 104171, 104172, 104173, 104174, 104175, 104234, 104235, 104236, 104237, 104238, 104239, 104298, 104299, 104300, 104301, 104302, 104360, 104361, 104362, 104363, 104364, 104365, 104423, 104424, 104425, 104426, 104427, 104428, 104486, 104487, 104488, 104489, 104490, 104491, 104550, 104551, 104552, 104553, 104554, 104555, 104614, 104615, 104616, 104617, 104618, 104619, 104677, 104678, 104679, 104680, 104681, 104738, 104739, 104740, 104741, 104742, 104743, 104802, 104803, 104804, 104805, 104806, 104864, 104865, 104866, 104867, 104868, 104869, 104927, 104928, 104929, 104930, 104931, 104988, 104989, 104990, 104991, 104992, 104993, 105050, 105051, 105052, 105053, 105054, 105055, 105112, 105113, 105114, 105115, 105116, 105117, 105174, 105175, 105176, 105177, 105178, 105179, 105236, 105237, 105238, 105239, 105240, 105241, 105298, 105299, 105300, 105301, 105302, 105303, 105360, 105361, 105362, 105363, 105364, 105365, 105422, 105423, 105424, 105425, 105426, 105427, 105484, 105485, 105486, 105487, 105488, 105489, 105547, 105548, 105549, 105550, 105551, 105552, 105610, 105611, 105612, 105613, 105614, 105615, 105673, 105674, 105675, 105676, 105677, 105678, 105736, 105737, 105738, 105739, 105740, 105741, 105799, 105800, 105801, 105802, 105803, 105804, 105862, 105863, 105864, 105865, 105866, 105867, 105925, 105926, 105927, 105928, 105929, 105930, 105988, 105989, 105990, 105991, 105992, 105993, 106051, 106052, 106053, 106054, 106055, 106056, 106114, 106115, 106116, 106117, 106118, 106119, 106176, 106177, 106178, 106179, 106180, 106181, 106237, 106238, 106239, 106240, 106241, 106242, 106243, 106301, 106302, 106303, 106304, 106305, 106306, 106364, 106365, 106366, 106367, 106368, 106369, 106427, 106428, 106429, 106430, 106431, 106432, 106489, 106490, 106491, 106492, 106493, 106494, 106495, 106553, 106554, 106555, 106556, 106557, 106558, 106615, 106616, 106617, 106618, 106619, 106620, 106621, 106679, 106680, 106681, 106682, 106683, 106684, 106741, 106742, 106743, 106744, 106745, 106746, 106747, 106806, 106807, 106808, 106809, 106810, 106811, 106869, 106870, 106871, 106872, 106873, 106874, 106875, 106933, 106934, 106935, 106936, 106937, 106938, 106992, 106995, 106996, 106997, 106998, 106999, 107000, 107001, 107058, 107059, 107060, 107061, 107062, 107063, 107116, 107119, 107120, 107121, 107122, 107123, 107124, 107125, 107184, 107185, 107186, 107187, 107188, 107189, 107244, 107247, 107248, 107249, 107250, 107251, 107252, 107253, 107312, 107313, 107314, 107315, 107316, 107317, 107372, 107375, 107376, 107377, 107378, 107379, 107380, 107381, 107438, 107439, 107440, 107441, 107442, 107443, 107444, 107498, 107501, 107502, 107503, 107504, 107505, 107506, 107507, 107564, 107565, 107566, 107567, 107568, 107569, 107570, 107624, 107627, 107628, 107629, 107630, 107631, 107632, 107633, 107691, 107692, 107693, 107694, 107695, 107696, 107697, 107752, 107755, 107756, 107757, 107758, 107759, 107760, 107761, 107819, 107820, 107821, 107822, 107823, 107824, 107825, 107880, 107883, 107884, 107885, 107886, 107887, 107888, 107889, 107947, 107948, 107949, 107950, 107951, 107952, 107953, 108008, 108011, 108012, 108013, 108014, 108015, 108016, 108017, 108072, 108075, 108076, 108077, 108078, 108079, 108080, 108081, 108136, 108139, 108140, 108141, 108142, 108143, 108144, 108145, 108200, 108203, 108204, 108205, 108206, 108207, 108208, 108209, 108264, 108267, 108268, 108269, 108270, 108271, 108272, 108273, 108328, 108331, 108332, 108333, 108334, 108335, 108336, 108337, 108392, 108395, 108396, 108397, 108398, 108399, 108400, 108401, 108456, 108459, 108460, 108461, 108462, 108463, 108464, 108465, 108520, 108523, 108524, 108525, 108526, 108527, 108528, 108529, 108584, 108587, 108588, 108589, 108590, 108591, 108592, 108593, 108648, 108650, 108651, 108652, 108653, 108654, 108655, 108656, 108657, 108712, 108715, 108716, 108717, 108719, 108720, 108721, 108776, 108778, 108779, 108780, 108781, 108783, 108784, 108785, 108840, 108843, 108844, 108845, 108846, 108847, 108848, 108849, 108904, 108906, 108907, 108908, 108909, 108910, 108911, 108912, 108913, 108968, 108971, 108972, 108973, 108974, 108975, 108976, 108977, 109032, 109034, 109035, 109036, 109037, 109038, 109039, 109040, 109041, 109095, 109098, 109099, 109100, 109101, 109102, 109103, 109104, 109158, 109160, 109161, 109162, 109163, 109164, 109165, 109166, 109167, 109221, 109224, 109225, 109226, 109228, 109229, 109230, 109283, 109284, 109286, 109287, 109288, 109289, 109291, 109292, 109293, 109346, 109349, 109350, 109351, 109353, 109354, 109355, 109408, 109410, 109411, 109412, 109413, 109415, 109416, 109417, 109471, 109474, 109475, 109476, 109478, 109479, 109480, 109533, 109534, 109536, 109537, 109538, 109539, 109541, 109542, 109543, 109598, 109601, 109602, 109603, 109605, 109606, 109607, 109661, 109662, 109664, 109665, 109666, 109667, 109669, 109670, 109671, 109726, 109728, 109729, 109730, 109731, 109733, 109734, 109735, 109789, 109790, 109792, 109793, 109794, 109795, 109797, 109798, 109799, 109853, 109855, 109856, 109857, 109858, 109860, 109861, 109915, 109916, 109918, 109919, 109920, 109921, 109923, 109924, 109980, 109982, 109983, 109984, 109985, 109987, 109988, 110043, 110044, 110046, 110047, 110048, 110049, 110051, 110052, 110108, 110110, 110111, 110112, 110113, 110115, 110116, 110171, 110172, 110174, 110175, 110176, 110177, 110179, 110180, 110236, 110238, 110239, 110240, 110241, 110243, 110244, 110299, 110300, 110302, 110303, 110304, 110305, 110307, 110308, 110363, 110364, 110366, 110367, 110368, 110369, 110371, 110372, 110427, 110428, 110430, 110431, 110432, 110433, 110435, 110436, 110491, 110492, 110494, 110495, 110496, 110497, 110499, 110500, 110555, 110556, 110558, 110559, 110560, 110561, 110563, 110564, 110619, 110620, 110622, 110623, 110624, 110625, 110627, 110628, 110683, 110684, 110686, 110687, 110688, 110689, 110691, 110692, 110747, 110748, 110750, 110751, 110752, 110755, 110756, 110811, 110812, 110814, 110815, 110816, 110819, 110820, 110874, 110875, 110877, 110878, 110879, 110882, 110883, 110937, 110938, 110940, 110941, 110942, 110945, 110946, 111001, 111002, 111004, 111005, 111006, 111007, 111009, 111010, 111065, 111066, 111068, 111069, 111070, 111071, 111073, 111074, 111128, 111129, 111131, 111132, 111133, 111134, 111136, 111137, 111191, 111192, 111194, 111195, 111196, 111197, 111199, 111200, 111255, 111256, 111258, 111259, 111260, 111261, 111263, 111264, 111319, 111320, 111322, 111323, 111324, 111325, 111327, 111328, 111382, 111383, 111385, 111386, 111387, 111390, 111445, 111446, 111447, 111448, 111449, 111450, 111453, 111509, 111510, 111512, 111513, 111514, 111517, 111518, 111573, 111574, 111575, 111576, 111577, 111578, 111581, 111582, 111637, 111638, 111640, 111641, 111642, 111645, 111646, 111701, 111702, 111703, 111704, 111705, 111706, 111709, 111710, 111765, 111766, 111768, 111769, 111770, 111773, 111774, 111829, 111830, 111831, 111832, 111833, 111834, 111837, 111838, 111892, 111893, 111895, 111896, 111897, 111900, 111901, 111954, 111955, 111956, 111957, 111958, 111959, 111960, 111963, 111964, 112019, 112020, 112022, 112023, 112024, 112027, 112082, 112083, 112084, 112085, 112086, 112087, 112088, 112091, 112146, 112147, 112148, 112149, 112150, 112151, 112154, 112208, 112209, 112210, 112211, 112212, 112213, 112214, 112217, 112273, 112274, 112275, 112276, 112277, 112278, 112281, 112336, 112337, 112338, 112339, 112340, 112341, 112342, 112345, 112401, 112402, 112403, 112404, 112405, 112406, 112409, 112464, 112465, 112466, 112467, 112468, 112469, 112470, 112473, 112528, 112529, 112530, 112531, 112532, 112533, 112536, 112590, 112591, 112592, 112593, 112594, 112595, 112596, 112599, 112655, 112656, 112657, 112658, 112659, 112660, 112663, 112718, 112719, 112720, 112721, 112722, 112723, 112724, 112727, 112781, 112782, 112783, 112784, 112785, 112786, 112787, 112790, 112844, 112845, 112846, 112847, 112848, 112849, 112850, 112853, 112907, 112908, 112909, 112910, 112911, 112912, 112916, 112970, 112971, 112972, 112973, 112974, 112975, 112979, 113033, 113034, 113035, 113036, 113037, 113038, 113039, 113042, 113096, 113097, 113098, 113099, 113100, 113101, 113102, 113105, 113160, 113161, 113162, 113163, 113164, 113165, 113166, 113169, 113224, 113225, 113226, 113227, 113228, 113229, 113230, 113233, 113288, 113289, 113290, 113291, 113292, 113293, 113297, 113352, 113353, 113354, 113355, 113356, 113357, 113361, 113415, 113416, 113417, 113418, 113419, 113420, 113424, 113478, 113479, 113480, 113481, 113482, 113483, 113487, 113541, 113542, 113543, 113544, 113545, 113546, 113604, 113605, 113606, 113607, 113608, 113609, 113667, 113668, 113669, 113670, 113671, 113672, 113730, 113731, 113732, 113733, 113734, 113735, 113792, 113793, 113794, 113795, 113796, 113797, 113801, 113854, 113855, 113856, 113857, 113858, 113859, 113863, 113918, 113919, 113920, 113921, 113922, 113923, 113927, 113982, 113983, 113984, 113985, 113986, 113987, 113991, 114045, 114046, 114047, 114048, 114049, 114050, 114054, 114108, 114109, 114110, 114111, 114112, 114113, 114117, 114172, 114173, 114174, 114175, 114176, 114177, 114181, 114236, 114237, 114238, 114239, 114240, 114241, 114245, 114300, 114301, 114303, 114304, 114305, 114360, 114363, 114364, 114365, 114367, 114368, 114369, 114428, 114429, 114431, 114432, 114433, 114487, 114490, 114491, 114492, 114494, 114495, 114496, 114552, 114553, 114555, 114556, 114557, 114610, 114613, 114614, 114615, 114617, 114618, 114619, 114678, 114679, 114681, 114682, 114683, 114738, 114741, 114742, 114743, 114745, 114746, 114747, 114806, 114807, 114809, 114810, 114811, 114866, 114869, 114870, 114871, 114873, 114874, 114875, 114934, 114935, 114937, 114938, 114939, 114994, 114997, 114998, 114999, 115001, 115002, 115003, 115058, 115061, 115062, 115063, 115065, 115066, 115067, 115122, 115125, 115126, 115127, 115129, 115130, 115131, 115185, 115188, 115189, 115190, 115192, 115193, 115248, 115251, 115252, 115253, 115255, 115256, 115312, 115315, 115316, 115317, 115319, 115320, 115321, 115376, 115379, 115380, 115381, 115383, 115384, 115385, 115440, 115443, 115444, 115445, 115447, 115448, 115504, 115507, 115508, 115509, 115511, 115512, 115566, 115569, 115570, 115571, 115573, 115574, 115628, 115631, 115632, 115633, 115635, 115636, 115692, 115695, 115696, 115697, 115699, 115700, 115756, 115759, 115760, 115761, 115763, 115764, 115819, 115822, 115823, 115824, 115826, 115827, 115882, 115885, 115886, 115887, 115889, 115890, 115946, 115949, 115950, 115951, 115953, 115954, 116010, 116013, 116014, 116015, 116017, 116018, 116074, 116077, 116078, 116081, 116082, 116138, 116141, 116142, 116145, 116146, 116202, 116205, 116206, 116207, 116209, 116210, 116266, 116269, 116270, 116271, 116273, 116274, 116330, 116333, 116334, 116335, 116337, 116338, 116394, 116397, 116398, 116399, 116401, 116402, 116458, 116461, 116462, 116465, 116466, 116521, 116522, 116524, 116525, 116526, 116529, 116530, 116585, 116588, 116589, 116592, 116593, 116647, 116648, 116650, 116651, 116652, 116655, 116656, 116712, 116715, 116716, 116719, 116720, 116775, 116776, 116778, 116779, 116780, 116783, 116784, 116838, 116841, 116842, 116845, 116846, 116899, 116900, 116902, 116903, 116904, 116907, 116908, 116964, 116967, 116968, 116971, 116972, 117027, 117028, 117030, 117031, 117032, 117035, 117036, 117092, 117094, 117095, 117096, 117099, 117100, 117155, 117156, 117158, 117159, 117160, 117163, 117164, 117220, 117222, 117223, 117224, 117227, 117228, 117283, 117284, 117286, 117287, 117288, 117291, 117292, 117348, 117350, 117351, 117352, 117355, 117411, 117412, 117414, 117415, 117416, 117419, 117474, 117475, 117477, 117478, 117479, 117482, 117537, 117538, 117540, 117541, 117542, 117545, 117600, 117601, 117603, 117604, 117605, 117608, 117663, 117664, 117666, 117667, 117668, 117671, 117726, 117727, 117729, 117730, 117731, 117734, 117789, 117790, 117792, 117793, 117794, 117797, 117853, 117854, 117856, 117857, 117858, 117861, 117917, 117918, 117920, 117921, 117922, 117925, 117981, 117982, 117984, 117985, 117986, 117989, 118045, 118046, 118048, 118049, 118050, 118053, 118109, 118110, 118112, 118113, 118114, 118117, 118173, 118174, 118176, 118177, 118178, 118181, 118237, 118238, 118239, 118240, 118244, 118300, 118301, 118302, 118303, 118307, 118363, 118364, 118366, 118367, 118371, 118427, 118428, 118430, 118431, 118435, 118490, 118491, 118493, 118494, 118498, 118553, 118554, 118556, 118557, 118561, 118617, 118618, 118620, 118621, 118625, 118681, 118682, 118683, 118684, 118685, 118689, 118744, 118745, 118747, 118748, 118752, 118807, 118808, 118809, 118810, 118811, 118815, 118871, 118874, 118875, 118879, 118934, 118935, 118937, 118938, 118939, 118943, 118998, 119001, 119002, 119006, 119060, 119061, 119063, 119064, 119065, 119069, 119125, 119127, 119128, 119132, 119187, 119188, 119189, 119190, 119191, 119195, 119251, 119254, 119255, 119314, 119315, 119317, 119318, 119319, 119379, 119382, 119383, 119442, 119443, 119445, 119446, 119447, 119506, 119507, 119509, 119510, 119511, 119570, 119571, 119573, 119574, 119575, 119634, 119635, 119637, 119638, 119639, 119698, 119699, 119701, 119702, 119703, 119761, 119762, 119764, 119765, 119766, 119824, 119825, 119827, 119828, 119829, 119888, 119889, 119891, 119892, 119893, 119952, 119953, 119955, 119956, 119957, 120016, 120017, 120019, 120020, 120080, 120081, 120083, 120084, 120144, 120145, 120147, 120148, 120149, 120208, 120209, 120211, 120212, 120213, 120271, 120272, 120274, 120275, 120334, 120335, 120337, 120338, 120398, 120399, 120401, 120402, 120462, 120463, 120465, 120466, 120523, 120524, 120526, 120527, 120584, 120585, 120587, 120588, 120648, 120649, 120651, 120652, 120708, 120712, 120713, 120715, 120716, 120771, 120775, 120776, 120778, 120779, 120834, 120838, 120839, 120841, 120842, 120901, 120904, 120905, 120960, 120964, 120967, 120968, 121028, 121031, 121032, 121088, 121091, 121092, 121095, 121096, 121156, 121159, 121160, 121216, 121219, 121220, 121223, 121224, 121282, 121285, 121286, 121340, 121343, 121344, 121347, 121348, 121403, 121407, 121410, 121411, 121466, 121469, 121470, 121473, 121474, 121529, 121533, 121536, 121537, 121592, 121595, 121596, 121599, 121600, 121656, 121660, 121663, 121664, 121720, 121723, 121724, 121727, 121728, 121783, 121786, 121787, 121790, 121791, 121846, 121849, 121850, 121853, 121854, 121909, 121912, 121913, 121916, 121917, 121972, 121975, 121976, 121979, 121980, 122036, 122039, 122040, 122043, 122100, 122103, 122104, 122107, 122164, 122167, 122168, 122171, 122228, 122231, 122232, 122235, 122292, 122295, 122296, 122299, 122359, 122360, 122363, 122423, 122424, 122427, 122487, 122488, 122491, 122550, 122551, 122554, 122613, 122614, 122617, 122673, 122674, 122677, 122733, 122734, 122737, 122797, 122801, 122861, 122865, 122924, 122928, 122983, 122987, 122991, 123047, 123050, 123054, 123109, 123110, 123113, 123117, 123174, 123177, 123181, 123237, 123238, 123240, 123241, 123245, 123300, 123303, 123307, 123361, 123362, 123364, 123365, 123369, 123423, 123427, 123431, 123485, 123488, 123489, 123493, 123548, 123552, 123556, 123611, 123614, 123615, 123619, 123674, 123678, 123682, 123740, 123741, 123745, 123801, 123802, 123862, 123863, 123924, 123925, 123929, 123986, 123987, 123991, 124047, 124048, 124108, 124109, 124169, 124230, 124293, 124356, 124416, 124418, 124477, 124479, 124538, 124541, 124601, 124604, 124663, 124666, 124725, 124728, 124787, 124790, 124791, 124849, 124852, 124911, 124914, 124973, 124976, 125036, 125039, 125099, 125102, 125162, 125165, 125225, 125227, 125228, 125286, 125343, 125344, 125405, 125465, 125466, 125591, 125651, 125714, 125717, 125774, 125777, 125778, 125834, 125837, 125893, 125896, 125897, 125952, 125955, 125956, 126014, 126017, 126018, 126076, 126079, 126136, 126139, 126140, 126196, 126199, 126257, 126260, 126261, 126317, 126320, 126377, 126380, 126381, 126438, 126441, 126497, 126500, 126556, 126559, 126615, 126618, 126674, 126677, 126734, 126737, 126794, 126856, 126859, 126980, 126983, 127102, 127157, 127161, 127219, 127273, 127277, 127329, 127332, 127384, 127387, 127443, 127495, 127499, 127553, 127602, 127605, 127654, 127658, 127708, 127712, 127759, 127763, 127810, 127814, 127864, 127867, 127918, 127921, 127973, 127976, 128031, 128080, 128186, 128290, 128343, 128394, 128443, 128494, 128546, 128597, 128648, 128968, 129075, 129125, 129379, 129429, 129478, 129528, 129578, 129624, 129670, 129719, 129769, 129817, 129866, 129916, 129964, 130011, 130058, 130595, 130687, 130777, 130821, 130863, 130903, 130944, 130984, 131025, 131068, 131155, 131238, 131280, 131326, 184772, 184775, 184777, 184779, 184781, 184783, 184784, 184786, 184788, 184789, 184790, 184791, 184792, 184793, 184794, 184795, 184796, 184797, 184798, 184799, 184800, 184801, 184802, 184803, 184804, 184805, 184806, 184807, 184808, 184810, 184811, 184812, 184813, 184815, 184816, 184818, 184819, 184821, 184822, 184823, 184825, 184826, 184827, 184830, 184831, 184833, 184835, 184836, 184840, 184844, 184846, 184847, 184852, 184856, 184858, 184864, 184868, 184870, 184876, 184883, 184884, 184890, 184897, 184898, 184904, 184911, 184912, 184913, 184919, 184927, 184928, 184936, 184944, 184945, 184946, 184953, 184954, 184963, 184964, 184965, 184972, 184973, 184974, 184982, 184983, 184984, 184993, 184995, 185003, 185005, 185014, 185016, 185026, 185028, 185037, 185039, 185050, 185052, 185063, 185065, 185078, 185080, 185093, 185095, 185111, 185112, 185113, 185129, 185130, 185131, 185147, 185148, 185149, 185166, 185168, 185185, 185186, 185187, 185188, 185205, 185207, 185224, 185225, 185226, 185227, 185244, 185246, 185263, 185264, 185265, 185266, 185283, 185284, 185285, 185286, 185303, 185304, 185305, 185306, 185323, 185325, 185326, 185343, 185345, 185346, 185363, 185365, 185366, 185384, 185386, 185387, 185404, 185406, 185407, 185427, 185429, 185430, 185447, 185449, 185450, 185471, 185473, 185474, 185493, 185495, 185496, 185519, 185520, 185521, 185522, 185544, 185545, 185546, 185547, 185571, 185572, 185573, 185574, 185597, 185598, 185599, 185600, 185625, 185626, 185627, 185628, 185653, 185654, 185655, 185656, 185681, 185682, 185683, 185684, 185685, 185710, 185711, 185712, 185713, 185738, 185739, 185740, 185741, 185742, 185767, 185768, 185769, 185770, 185771, 185796, 185797, 185798, 185799, 185800, 185825, 185826, 185827, 185828, 185829, 185855, 185856, 185857, 185858, 185859, 185884, 185886, 185887, 185888, 185914, 185915, 185916, 185917, 185918, 185943, 185945, 185946, 185947, 185975, 185977, 185978, 185979, 186004, 186006, 186007, 186008, 186037, 186039, 186040, 186041, 186067, 186069, 186070, 186071, 186103, 186104, 186105, 186106, 186107, 186136, 186138, 186139, 186140, 186173, 186174, 186175, 186176, 186177, 186208, 186210, 186211, 186212, 186246, 186247, 186248, 186249, 186250, 186284, 186286, 186287, 186288, 186322, 186323, 186324, 186325, 186326, 186327, 186360, 186361, 186362, 186363, 186364, 186400, 186401, 186402, 186403, 186404, 186405, 186440, 186441, 186442, 186443, 186444, 186480, 186481, 186482, 186483, 186484, 186485, 186488, 186520, 186521, 186522, 186523, 186524, 186525, 186563, 186564, 186565, 186566, 186567, 186568, 186571, 186604, 186605, 186606, 186607, 186608, 186609, 186612, 186646, 186647, 186648, 186649, 186650, 186651, 186687, 186688, 186689, 186690, 186691, 186692, 186732, 186733, 186734, 186735, 186736, 186737, 186740, 186769, 186771, 186772, 186773, 186774, 186777, 186808, 186810, 186811, 186812, 186813, 186816, 186844, 186846, 186847, 186848, 186849, 186852, 186886, 186888, 186889, 186890, 186891, 186894, 186926, 186928, 186929, 186930, 186931, 186934, 186969, 186971, 186972, 186973, 186974, 186977, 187012, 187014, 187015, 187016, 187017, 187020, 187056, 187058, 187059, 187060, 187061, 187064, 187099, 187101, 187102, 187103, 187104, 187107, 187142, 187144, 187145, 187146, 187147, 187149, 187150, 187185, 187187, 187188, 187189, 187190, 187193, 187228, 187229, 187230, 187231, 187232, 187233, 187234, 187235, 187236, 187271, 187273, 187274, 187275, 187276, 187277, 187279, 187313, 187314, 187315, 187316, 187317, 187318, 187319, 187320, 187321, 187355, 187356, 187357, 187358, 187359, 187360, 187361, 187363, 187397, 187398, 187399, 187400, 187401, 187402, 187404, 187405, 187439, 187440, 187441, 187442, 187443, 187444, 187446, 187447, 187482, 187483, 187484, 187485, 187486, 187487, 187488, 187489, 187490, 187525, 187526, 187527, 187528, 187529, 187530, 187532, 187533, 187568, 187569, 187570, 187571, 187572, 187573, 187574, 187575, 187576, 187612, 187613, 187614, 187615, 187616, 187618, 187619, 187657, 187658, 187659, 187660, 187661, 187663, 187664, 187700, 187701, 187702, 187703, 187704, 187706, 187707, 187746, 187747, 187748, 187749, 187750, 187751, 187752, 187753, 187790, 187791, 187792, 187793, 187794, 187796, 187797, 187837, 187838, 187839, 187840, 187841, 187842, 187844, 187845, 187884, 187885, 187886, 187887, 187888, 187890, 187891, 187933, 187934, 187935, 187936, 187937, 187939, 187940, 187989, 187990, 187991, 187992, 187993, 187995, 187996, 188039, 188040, 188041, 188042, 188043, 188045, 188046, 188095, 188096, 188097, 188098, 188099, 188101, 188102, 188144, 188145, 188146, 188147, 188148, 188149, 188150, 188151, 188199, 188200, 188201, 188202, 188203, 188205, 188206, 188255, 188256, 188257, 188258, 188259, 188260, 188261, 188262, 188311, 188312, 188313, 188314, 188315, 188317, 188318, 188364, 188365, 188366, 188367, 188368, 188369, 188370, 188371, 188372, 188418, 188419, 188420, 188421, 188422, 188423, 188424, 188425, 188426, 188473, 188474, 188475, 188476, 188477, 188478, 188479, 188480, 188481, 188528, 188529, 188530, 188531, 188532, 188533, 188534, 188535, 188536, 188584, 188585, 188586, 188587, 188588, 188590, 188591, 188592, 188640, 188641, 188642, 188643, 188644, 188645, 188646, 188647, 188648, 188697, 188698, 188699, 188700, 188703, 188704, 188705, 188753, 188754, 188755, 188756, 188757, 188758, 188759, 188760, 188761, 188811, 188812, 188813, 188817, 188818, 188819, 188867, 188868, 188869, 188870, 188871, 188872, 188873, 188874, 188875, 188928, 188929, 188934, 188935, 188936, 188987, 188988, 188989, 188990, 188991, 188992, 188993, 188994, 188995, 189048, 189049, 189054, 189055, 189056, 189107, 189108, 189109, 189110, 189111, 189113, 189114, 189115, 189168, 189170, 189174, 189175, 189176, 189229, 189230, 189231, 189232, 189233, 189235, 189236, 189237, 189290, 189292, 189296, 189297, 189298, 189351, 189352, 189353, 189354, 189355, 189357, 189358, 189359, 189412, 189418, 189419, 189420, 189473, 189474, 189475, 189476, 189477, 189478, 189479, 189480, 189481, 189540, 189541, 189542, 189595, 189596, 189597, 189598, 189599, 189600, 189601, 189602, 189603, 189662, 189663, 189664, 189717, 189718, 189719, 189721, 189722, 189723, 189724, 189725, 189784, 189785, 189786, 189839, 189840, 189841, 189842, 189843, 189844, 189845, 189846, 189847, 189906, 189907, 189908, 189961, 189962, 189963, 189964, 189965, 189966, 189967, 189968, 189969, 190027, 190028, 190029, 190081, 190082, 190087, 190088, 190089, 190147, 190148, 190149, 190201, 190202, 190207, 190208, 190209, 190268, 190269, 190270, 190323, 190329, 190330, 190331, 190388, 190389, 190390, 190441, 190447, 190448, 190449, 190508, 190509, 190510, 190569, 190570, 190571, 190629, 190630, 190631, 190689, 190690, 190691, 190749, 190750, 190751, 190809, 190810, 190811, 190869, 190870, 190871, 190929, 190930, 190931, 190989, 190990, 190991, 191049, 191050, 191051, 191109, 191110, 191111, 191169, 191170, 191171, 191229, 191230, 191231, 191289, 191290, 191291, 191350, 191351, 191352, 191411, 191412, 191413, 191472, 191473, 191474, 191475, 191533, 191534, 191535, 191536, 191592, 191593, 191594, 191651, 191652, 191653, 191710, 191711, 191712, 191713, 191769, 191770, 191771, 191772, 191829, 191830, 191831, 191832, 191889, 191890, 191891, 191892, 191949, 191950, 191951, 191952, 192009, 192010, 192011, 192012, 192069, 192070, 192071, 192072, 192129, 192130, 192131, 192132, 192187, 192188, 192189, 192245, 192246, 192247, 192305, 192306, 192307, 192308, 192365, 192366, 192367, 192425, 192426, 192427, 192428, 192485, 192486, 192487, 192488, 192545, 192546, 192547, 192548, 192605, 192606, 192607, 192608, 192664, 192665, 192666, 192667, 192723, 192724, 192725, 192783, 192784, 192785, 192843, 192844, 192903, 192904, 192905, 192963, 192964, 193022, 193023, 193025, 193080, 193081, 193083, 193138, 193139, 193140, 193141, 193196, 193197, 193198, 193199, 193255, 193256, 193257, 193258, 193314, 193315, 193316, 193317, 193373, 193374, 193375, 193376, 193432, 193433, 193434, 193435, 193491, 193492, 193493, 193494, 193550, 193551, 193552, 193553, 193609, 193610, 193611, 193612, 193668, 193669, 193670, 193671, 193727, 193728, 193729, 193730, 193786, 193787, 193788, 193789, 193844, 193845, 193846, 193847, 193902, 193903, 193904, 193905, 193961, 193962, 193963, 193964, 194020, 194022, 194023, 194079, 194081, 194082, 194138, 194140, 194141, 194197, 194198, 194199, 194200, 194256, 194258, 194259, 194315, 194316, 194317, 194318, 194374, 194375, 194376, 194377, 194432, 194433, 194434, 194435, 194490, 194491, 194492, 194493, 194549, 194550, 194551, 194552, 194608, 194609, 194610, 194667, 194668, 194669, 194726, 194727, 194728, 194785, 194786, 194787, 194844, 194845, 194846, 194903, 194904, 194905, 194906, 194962, 194963, 194964, 194965, 195021, 195022, 195023, 195024, 195080, 195081, 195082, 195083, 195139, 195140, 195141, 195142, 195200, 195201, 195202, 195258, 195259, 195260, 195317, 195318, 195319, 195377, 195378, 195379, 195438, 195439, 195440, 195498, 195499, 195500, 195501, 195558, 195559, 195560, 195561, 195618, 195619, 195620, 195621, 195678, 195679, 195680, 195681, 195738, 195739, 195740, 195741, 195799, 195800, 195801, 195802, 195859, 195860, 195861, 195862, 195919, 195920, 195921, 195922, 195980, 195981, 195982, 195983, 196041, 196042, 196043, 196044, 196102, 196103, 196104, 196105, 196163, 196164, 196165, 196166, 196224, 196225, 196226, 196227, 196285, 196286, 196287, 196288, 196345, 196346, 196347, 196348, 196405, 196406, 196407, 196408, 196465, 196466, 196467, 196468, 196525, 196526, 196527, 196528, 196586, 196587, 196588, 196589, 196647, 196648, 196649, 196650, 196705, 196706, 196707, 196708, 196763, 196764, 196765, 196766, 196823, 196824, 196825, 196826, 196882, 196883, 196884, 196885, 196940, 196941, 196942, 196943, 196999, 197000, 197001, 197056, 197057, 197058, 197059, 197113, 197114, 197115, 197168, 197169, 197170, 197171, 197224, 197225, 197226, 197280, 197281, 197282, 197283, 197336, 197337, 197338, 197391, 197392, 197393, 197394, 197447, 197448, 197449, 197450, 197503, 197504, 197505, 197506, 197559, 197560, 197561, 197613, 197614, 197615, 197616, 197668, 197669, 197670, 197722, 197723, 197724, 197725, 197777, 197778, 197779, 197780, 197832, 197833, 197834, 197885, 197886, 197887, 197888, 197939, 197940, 197989, 197990, 197991, 197992, 198041, 198042, 198089, 198090, 198091, 198092, 198143, 198144, 198193, 198194, 198195, 198196, 198241, 198242, 198286, 198287, 198288, 198289, 198334, 198335, 198380, 198381, 198382, 198383, 198424, 198425, 198426, 198467, 198468, 198469, 198470, 198514, 198515, 198558, 198560, 198561, 198602, 198603, 198604, 198645, 198646, 198647, 198688, 198689, 198690, 198731, 198732, 198733, 198776, 198817, 198819, 198820, 198859, 198896, 198897, 198898, 198899, 198937, 198973, 198974, 198975, 198976, 199012, 199047, 199048, 199084, 199120, 199153, 199154, 199184, 199186, 199187, 199219, 199220, 199253, 199254, 199285, 199286, 199317, 199318, 199350, 199351, 199384, 199385, 199411, 199412, 199439, 199440, 199465, 199466, 199492, 199493, 199515, 199516, 199539, 199540, 199558, 199559, 199578, 199579, 199597, 199598, 199617, 199618, 199635, 199636, 199654, 199655, 199672, 199673, 199689, 199690, 199709, 199729, 199745, 199746, 199761, 199762, 199778, 199779, 199795, 199796], "numberOfPointsInBox": 5119}, "attributes": {"trackId": "left_second_track"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}, {"id": "cff15e77-6e63-4d8c-b67b-ac45c96cd684", "objectId": "e4366b9f-a350-42a9-8ac2-61eeeee5513c", "className": "3D_signal_pole", "geometry": {"associatedPoints": [159, 5760, 5761, 13438, 16540, 16541, 16853, 16855, 16856, 16857, 16858, 27163, 27164, 31412, 31415, 31416], "numberOfPointsInBox": 16}, "attributes": {"structure": "solid"}, "sensor": {"type": "LIDAR", "uri": "lidar_merged/000_1632321880.132833000.pcd", "timestamp": "1632321880.132833000"}}]}}]} diff --git a/tests/__test_assets__/understand_ai_real_life.json.license b/tests/__test_assets__/understand_ai_real_life.json.license deleted file mode 100644 index fbb8ddf..0000000 --- a/tests/__test_assets__/understand_ai_real_life.json.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: Copyright DB InfraGO AG and the raillabel contributors -SPDX-License-Identifier: Apache-2.0 diff --git a/tests/__test_assets__/understand_ai_t4_short.json b/tests/__test_assets__/understand_ai_t4_short.json deleted file mode 100644 index c22a41e..0000000 --- a/tests/__test_assets__/understand_ai_t4_short.json +++ /dev/null @@ -1,823 +0,0 @@ -{ - "metadata": { - "clip_id": "db_3_2021-09-22-14-28-01_2021-09-22-14-44-03", - "external_clip_id": "2021-09-22-14-28-01_2021-09-22-14-44-03", - "project_id": "trains_4", - "export_time": "2023-04-20 01:38 UTC", - "exporter_version": "1.0.0", - "coordinate_system_3d": "FLU", - "coordinate_system_reference": "SENSOR", - "folder_name": "2021-09-22-14-28-01_2021-09-22-14-44-03" - }, - "coordinateSystems": [ - { - "coordinate_system_id": "rgb_middle_rect", - "topic": "/S1206063/image", - "frame_id": "S1206063", - "position": [ - 0, - 1, - 2 - ], - "rotation_quaternion": [ - 0.97518507, - -0.18529384, - -0.05469746, - -0.10811315 - ], - "rotation_matrix": [ - 0.9253488, - -0.3732186, - -0.0666149, - -0.3495646, - -0.9079555, - 0.2311309, - -0.1467457, - -0.1905905, - -0.9706395 - ], - "angle_axis_rotation": [ - 0.9809347, - -0.1863863, - -0.05502 - ], - "homogeneous_transform": [ - 0.9999680667369587, - -0.005432933661727504, - -0.005860779656024867, - 0.06694577073389593, - 0.005410226430795589, - 0.9999778242364062, - -0.003883360064516633, - -0.0009111521949818156, - 0.005881747726375861, - 0.003851527911158113, - 0.9999752850828029, - 2.054322280217477, - 0, - 0, - 0, - 1 - ], - "measured_position": [ - 0, - 0, - 0 - ], - "camera_matrix": [ - 0.48, - 0, - 0.81, - 0, - 0.16, - 0.83, - 0, - 0, - 1 - ], - "dist_coeffs": [ - 0.49, - 0.69, - 0.31, - 0.81, - 0.99 - ] - }, - { - "coordinate_system_id": "ir_middle", - "topic": "/A0001781/image", - "frame_id": "A0001781", - "position": [ - 0, - 2, - 1 - ], - "rotation_quaternion": [ - -0.64984101, - -0.72563166, - 0.18784818, - -0.12600959 - ], - "rotation_matrix": [ - -0.1236565, - 0.9904318, - -0.0612698, - 0.8957491, - 0.0848394, - -0.4363896, - -0.427016, - -0.1088448, - -0.8976693 - ], - "angle_axis_rotation": [ - -0.6550625, - -0.7314621, - 0.1893575 - ], - "homogeneous_transform": [ - 0.998808097970439, - -0.003596303890419965, - 0.04867699689794021, - 0.09334241552161804, - 0.0039172823670546, - 0.9999712003585413, - -0.006500257868352383, - 0.2252408177142321, - -0.04865221811522157, - 0.006683191739435895, - 0.9987934203931477, - 3.517100000000001, - 0, - 0, - 0, - 1 - ], - "measured_position": [ - 0, - 0, - 0 - ], - "camera_matrix": [ - 0.47, - 0, - 0.85, - 0, - 0.15, - 0.23, - 0, - 0, - 1 - ], - "dist_coeffs": [ - 0.19, - 0.66, - 0.31, - 0.21, - 0.99 - ] - }, - { - "coordinate_system_id": "lidar_merged", - "topic": "/lidar_merged", - "frame_id": "lidar_merged", - "position": [ - 0, - 0, - 0 - ], - "rotation_quaternion": [ - 0, - 0, - 0, - 1 - ], - "rotation_matrix": [ - 1, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 1 - ], - "angle_axis_rotation": [ - 0, - 0, - 0 - ], - "homogeneous_transform": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ] - }, - { - "coordinate_system_id": "radar", - "rotation_around_z_in_degrees": 1.22869, - "topic": "/talker1/Nvt/Cartesian", - "frame_id": "navtech", - "position": [ - 2, - 0, - 1 - ], - "rotation_quaternion": [ - -0.64984101, - 0.18784818, - -0.72563166, - -0.12600959 - ], - "rotation_matrix": [ -0.1236565, -0.4270160, 0.8957491, - -0.0612698, -0.8976693, -0.4363896, - 0.9904318, -0.1088448, 0.0848394 ], - "angle_axis_rotation": [ - -0.6550625, 0.1893575, -0.7314621 - ], - "homogeneous_transform": [ - 0.9997700727912402, - -0.02144298372425017, - 0, - -0.002940099322541904, - 0.02144298372425017, - 0.9997700727912402, - 0, - 0.3296990382394244, - 0, - 0, - 1, - 1.5638, - 0, - 0, - 0, - 1 - ] - } - ], - "frames": [ - { - "frameId": "000", - "timestamp": "1632321743.134149", - "annotations": { - "2D_BOUNDING_BOX": [ - { - "id": "78f0ad89-2750-4a30-9d66-44c9da73a714", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "2D_person", - "geometry": { - "xMin": -1.0, - "yMin": -0.5, - "xMax": 1, - "yMax": 2.5 - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 0, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "rgb_middle_rect", - "uri": "S1206063/rgb_test0.png", - "timestamp": "1632321743.100000072" - } - }, - { - "id": "68b4e02c-40c8-4de0-89ad-bc00ed05a043", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "2D_person", - "geometry": { - "xMin": 2.5, - "yMin": 2.0, - "xMax": 3.5, - "yMax": 2.0 - }, - "attributes": { - "test_bool_attr0": false - }, - "sensor": { - "type": "ir_middle", - "timestamp": "1632321743.106000004", - "uri": "A0001781/ir_test0.png" - } - } - ], - "2D_POLYLINE": [ - { - "id": "bebfbae4-61a2-4758-993c-efa846b050a5", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "2D_person", - "geometry": { - "points": [ - [ - 1.0, - 2.0 - ], - [ - 3.0, - 4.0 - ] - ] - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 2, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "rgb_middle_rect", - "uri": "S1206063/rgb_test0.png", - "timestamp": "1632321743.100000072" - } - } - ], - "2D_POLYGON": [ - { - "id": "3f63201c-fb33-4487-aff6-ae0aa5fa976c", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "2D_person", - "geometry": { - "points": [ - [ - 7.0, - 6.0 - ], - [ - 5.0, - 4.0 - ], - [ - 3.0, - 2.0 - ], - [ - 1.0, - 0.0 - ] - ] - }, - "attributes": { - "test_bool_attr0": false - }, - "sensor": { - "type": "ir_middle", - "timestamp": "1632321743.106000004", - "uri": "A0001781/ir_test0.png" - } - } - ], - "3D_BOUNDING_BOX": [ - { - "id": "dc2be700-8ee4-45c4-9256-920b5d55c917", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "3D_person", - "geometry": { - "center": { - "x": 0.49, - "y": 0.04, - "z": 0.73 - }, - "quaternion": { - "x": 0.0, - "y": 0.0, - "z": 0.0, - "w": 0.0 - }, - "size": { - "width": 0.75, - "length": 0.01, - "height": 0.1 - } - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 0, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.134149", - "uri": "lidar_merged/lidar_test0.pcd" - } - }, - { - "id": "450ceb81-9778-4e63-bf89-42f3ed9f6747", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "3D_person", - "geometry": { - "center": { - "x": 0.0, - "y": 1.0, - "z": 2.0 - }, - "quaternion": { - "x": 3.0, - "y": 4.0, - "z": 5.0, - "w": 6.0 - }, - "size": { - "width": 7.0, - "length": 8.0, - "height": 9.0 - } - }, - "attributes": { - "test_bool_attr0": false - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.134149", - "uri": "lidar_merged/lidar_test0.pcd" - } - } - ], - "3D_SEGMENTATION": [ - { - "id": "c1087f1d-7271-4dee-83ad-519a4e3b78a8", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "3D_person", - "geometry": { - "associatedPoints": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "numberOfPointsInBox": 10 - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 0, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.134149", - "uri": "lidar_merged/lidar_test0.pcd" - } - }, - { - "id": "50be7fe3-1f43-47ca-b65a-930e6cfacfeb", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "3D_person", - "geometry": { - "associatedPoints": [ - 9, - 8, - 7, - 6, - 5, - 4, - 3, - 2, - 1, - 0 - ], - "numberOfPointsInBox": 10 - }, - "attributes": { - "test_bool_attr0": false - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.134149", - "uri": "lidar_merged/lidar_test0.pcd" - } - } - ] - } - }, - { - "frameId": "001", - "timestamp": "1632321743.233263", - "annotations": { - "2D_BOUNDING_BOX": [ - { - "id": "6ba42cbc-484e-4b8d-a022-b23c2bb6643c", - "objectId": "6fe55546-0dd7-4e40-b6b4-bb7ea3445772", - "className": "2D_person", - "geometry": { - "xMin": -1.0, - "yMin": -0.5, - "xMax": 1, - "yMax": 2.5 - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 0, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "rgb_middle_rect", - "uri": "S1206063/rgb_test1.png", - "timestamp": "1632321743.2" - } - }, - { - "id": "5f28fa18-8f2a-4a40-a0b6-c0bbedc00f2e", - "objectId": "6fe55546-0dd7-4e40-b6b4-bb7ea3445772", - "className": "2D_person", - "geometry": { - "xMin": 2.5, - "yMin": 2.0, - "xMax": 3.5, - "yMax": 2.0 - }, - "attributes": { - "test_bool_attr0": false - }, - "sensor": { - "type": "ir_middle", - "timestamp": "1632321743.208000004", - "uri": "A0001781/ir_test1.png" - } - } - ], - "2D_POLYLINE": [ - { - "id": "e2503c5d-9fe4-4666-b510-ef644c5a766b", - "objectId": "6fe55546-0dd7-4e40-b6b4-bb7ea3445772", - "className": "2D_person", - "geometry": { - "points": [ - [ - 1.0, - 2.0 - ], - [ - 3.0, - 4.0 - ] - ] - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 0, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "rgb_middle_rect", - "uri": "S1206063/rgb_test1.png", - "timestamp": "1632321743.2" - } - } - ], - "2D_POLYGON": [], - "3D_BOUNDING_BOX": [ - { - "id": "536ac83a-32c8-4fce-8499-ef32716c64a6", - "objectId": "22dedd49-6dcb-413b-87ef-00ccfb532e98", - "className": "3D_train", - "geometry": { - "center": { - "x": 0.0, - "y": 1.0, - "z": 2.0 - }, - "quaternion": { - "x": 3.0, - "y": 4.0, - "z": 5.0, - "w": 6.0 - }, - "size": { - "width": 7.0, - "length": 8.0, - "height": 9.0 - } - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 0, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.233263", - "uri": "lidar_merged/lidar_test1.pcd" - } - }, - { - "id": "e53bd5e3-980a-4fa7-a0f9-5a2e59ba663c", - "objectId": "22dedd49-6dcb-413b-87ef-00ccfb532e98", - "className": "3D_train", - "geometry": { - "center": { - "x": 0.0, - "y": 1.0, - "z": 2.0 - }, - "quaternion": { - "x": 3.0, - "y": 4.0, - "z": 5.0, - "w": 6.0 - }, - "size": { - "width": 7.0, - "length": 8.0, - "height": 9.0 - } - }, - "attributes": { - "test_bool_attr0": false - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.233263", - "uri": "lidar_merged/lidar_test1.pcd" - } - } - ], - "3D_SEGMENTATION": [ - { - "id": "550df2c3-0e66-483e-bcc6-f3013b7e581b", - "objectId": "22dedd49-6dcb-413b-87ef-00ccfb532e98", - "className": "3D_train", - "geometry": { - "associatedPoints": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "numberOfPointsInBox": 10 - }, - "attributes": { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - "test_num_attr0": 0, - "test_num_attr1": 1, - "test_bool_attr0": true, - "test_vec_attr0": [ - "0", - "1" - ], - "test_vec_attr1": [ - 0, - 1, - 2 - ], - "test_vec_attr2": [ - "a", - "b", - "c" - ] - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.233263", - "uri": "lidar_merged/lidar_test1.pcd" - } - }, - { - "id": "12b21c52-06ea-4269-9805-e7167e7a74ed", - "objectId": "22dedd49-6dcb-413b-87ef-00ccfb532e98", - "className": "3D_train", - "geometry": { - "associatedPoints": [ - 9, - 8, - 7, - 6, - 5, - 4, - 3, - 2, - 1, - 0 - ], - "numberOfPointsInBox": 10 - }, - "attributes": { - "test_bool_attr0": false - }, - "sensor": { - "type": "lidar_merged", - "timestamp": "1632321743.233263", - "uri": "lidar_merged/lidar_test1.pcd" - } - } - ] - } - } - ] -} diff --git a/tests/__test_assets__/understand_ai_t4_short.json.license b/tests/__test_assets__/understand_ai_t4_short.json.license deleted file mode 100644 index fbb8ddf..0000000 --- a/tests/__test_assets__/understand_ai_t4_short.json.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: Copyright DB InfraGO AG and the raillabel contributors -SPDX-License-Identifier: Apache-2.0 diff --git a/tests/conftest.py b/tests/conftest.py index 4e635c6..06b81e2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,8 @@ # Copyright DB InfraGO AG and contributors # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + import glob import json import sys @@ -13,11 +15,50 @@ sys.path.insert(1, str(Path(__file__).parent.parent)) import raillabel + +from format.test_attributes import attributes_multiple_types, attributes_multiple_types_json +from format.test_bbox import bbox, bbox_json, bbox_id +from format.test_camera import camera, camera_json, camera_empty +from format.test_cuboid import cuboid, cuboid_json, cuboid_id +from format.test_frame import frame, frame_json +from format.test_frame_interval import frame_interval, frame_interval_json +from format.test_intrinsics_pinhole import intrinsics_pinhole, intrinsics_pinhole_json +from format.test_intrinsics_radar import intrinsics_radar, intrinsics_radar_json +from format.test_lidar import lidar, lidar_json +from format.test_metadata import metadata, metadata_json +from format.test_num import num, num_json, num_id +from format.test_object import ( + objects, + object_person, + object_person_json, + object_person_id, + object_track, + object_track_json, + object_track_id, +) +from format.test_point2d import point2d, point2d_json, another_point2d, another_point2d_json +from format.test_point3d import point3d, point3d_json, another_point3d, another_point3d_json +from format.test_poly2d import poly2d, poly2d_json, poly2d_id +from format.test_poly3d import poly3d, poly3d_json, poly3d_id +from format.test_quaternion import quaternion, quaternion_json +from format.test_radar import radar, radar_json, radar_empty +from format.test_size2d import size2d, size2d_json +from format.test_size3d import size3d, size3d_json +from format.test_seg3d import seg3d, seg3d_json, seg3d_id +from format.test_sensor_reference import ( + another_sensor_reference, + another_sensor_reference_json, + sensor_reference, + sensor_reference_json, +) +from format.test_transform import transform, transform_json + + json_data_directories = [ Path(__file__).parent / "__test_assets__", - Path(__file__).parent.parent / "raillabel" / "validate" / "schemas" ] + @pytest.fixture(scope="session", autouse=True) def compile_uncommented_test_file(): """Compiles the main test file from json5 to json.""" @@ -30,8 +71,9 @@ def compile_uncommented_test_file(): with open(str(json5_file_path)[:-1], "w") as f: json.dump(data, f, indent=4) + @pytest.fixture -def json_paths(request) -> t.Dict[str, Path]: +def json_paths(request) -> dict[str, Path]: json_paths = _fetch_json_paths_from_cache(request) if json_paths is None: @@ -39,10 +81,12 @@ def json_paths(request) -> t.Dict[str, Path]: return json_paths -def _fetch_json_paths_from_cache(request) -> t.Optional[t.Dict[str, Path]]: + +def _fetch_json_paths_from_cache(request) -> dict[str, Path] | None: return request.config.cache.get("json_paths", None) -def _collect_json_paths() -> t.List[Path]: + +def _collect_json_paths() -> list[Path]: json_paths = [] for dir in json_data_directories: @@ -50,6 +94,7 @@ def _collect_json_paths() -> t.List[Path]: return json_paths + def _get_file_identifier(path: Path) -> str: """Return relative path from test asset dir as string.""" @@ -59,15 +104,16 @@ def _get_file_identifier(path: Path) -> str: test_assets_dir_index = path.parts.index("__test_assets__") relative_path = "" - for part in path.parts[test_assets_dir_index+1:-1]: + for part in path.parts[test_assets_dir_index + 1 : -1]: relative_path += part + "/" relative_path += path.stem return relative_path + @pytest.fixture -def json_data(request) -> t.Dict[str, dict]: +def json_data(request) -> dict[str, dict]: json_data = _fetch_json_data_from_cache(request) if json_data is None: @@ -75,9 +121,11 @@ def json_data(request) -> t.Dict[str, dict]: return json_data -def _fetch_json_data_from_cache(request) -> t.Optional[t.Dict[str, Path]]: + +def _fetch_json_data_from_cache(request) -> dict[str, Path] | None: return request.config.cache.get("json_data", None) + def _load_json_data(path: Path) -> dict: with path.open() as f: json_data = json.load(f) diff --git a/tests/filter/test_filter.py b/tests/filter/test_filter.py new file mode 100644 index 0000000..b9fd9c4 --- /dev/null +++ b/tests/filter/test_filter.py @@ -0,0 +1,266 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +import raillabel +from raillabel.scene_builder import SceneBuilder + + +def test_include_frame_ids(): + scene = SceneBuilder.empty().add_frame(1).add_frame(2).add_frame(3).result + filters = [raillabel.filter.IncludeFrameIdFilter([1, 3])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_frame(1).add_frame(3).result + + +def test_exclude_frame_ids(): + scene = SceneBuilder.empty().add_frame(1).add_frame(2).add_frame(3).result + filters = [raillabel.filter.ExcludeFrameIdFilter([2])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_frame(1).add_frame(3).result + + +def test_start_time(): + scene = SceneBuilder.empty().add_frame(1, 100).add_frame(2, 200).add_frame(3, 300).result + filters = [raillabel.filter.StartTimeFilter(150)] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_frame(2, 200).add_frame(3, 300).result + + +def test_end_time(): + scene = SceneBuilder.empty().add_frame(1, 100).add_frame(2, 200).add_frame(3, 300).result + filters = [raillabel.filter.EndTimeFilter(250)] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_frame(1, 100).add_frame(2, 200).result + + +def test_include_annotation_ids(): + scene = ( + SceneBuilder.empty() + .add_bbox(uid="6c95543d-0000-4000-0000-000000000000") + .add_bbox(uid="6c95543d-0000-4000-0000-000000000001") + .add_bbox(uid="6c95543d-0000-4000-0000-000000000002") + .result + ) + filters = [ + raillabel.filter.IncludeAnnotationIdFilter( + [ + UUID("6c95543d-0000-4000-0000-000000000000"), + UUID("6c95543d-0000-4000-0000-000000000002"), + ] + ) + ] + + actual = scene.filter(filters) + assert ( + actual + == SceneBuilder.empty() + .add_bbox(uid="6c95543d-0000-4000-0000-000000000000") + .add_bbox(uid="6c95543d-0000-4000-0000-000000000002") + .result + ) + + +def test_exclude_annotation_ids(): + scene = ( + SceneBuilder.empty() + .add_bbox(uid="6c95543d-0000-4000-0000-000000000000") + .add_bbox(uid="6c95543d-0000-4000-0000-000000000001") + .add_bbox(uid="6c95543d-0000-4000-0000-000000000002") + .result + ) + filters = [ + raillabel.filter.ExcludeAnnotationIdFilter([UUID("6c95543d-0000-4000-0000-000000000001")]) + ] + + actual = scene.filter(filters) + assert ( + actual + == SceneBuilder.empty() + .add_bbox(uid="6c95543d-0000-4000-0000-000000000000") + .add_bbox(uid="6c95543d-0000-4000-0000-000000000002") + .result + ) + + +def test_include_annotation_type(): + scene = SceneBuilder.empty().add_bbox().add_cuboid().result + filters = [raillabel.filter.IncludeAnnotationTypeFilter(["bbox"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox().result + + +def test_exclude_annotation_type(): + scene = SceneBuilder.empty().add_bbox().add_cuboid().result + filters = [raillabel.filter.ExcludeAnnotationTypeFilter(["cuboid"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox().result + + +def test_include_object_ids(): + scene = ( + SceneBuilder.empty() + .add_bbox(object_name="person_0001") + .add_cuboid(object_name="train_0001") + .result + ) + filters = [ + raillabel.filter.IncludeObjectIdFilter([UUID("5c59aad4-0000-4000-0000-000000000000")]) + ] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(object_name="person_0001").result + + +def test_exclude_object_ids(): + scene = ( + SceneBuilder.empty() + .add_bbox(object_name="person_0001") + .add_cuboid(object_name="train_0001") + .result + ) + filters = [ + raillabel.filter.ExcludeObjectIdFilter([UUID("5c59aad4-0000-4000-0000-000000000001")]) + ] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(object_name="person_0001").result + + +def test_include_object_types(): + scene = ( + SceneBuilder.empty() + .add_bbox(object_name="person_0001") + .add_cuboid(object_name="train_0001") + .result + ) + filters = [raillabel.filter.IncludeObjectTypeFilter(["person"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(object_name="person_0001").result + + +def test_exclude_object_types(): + scene = ( + SceneBuilder.empty() + .add_bbox(object_name="person_0001") + .add_cuboid(object_name="train_0001") + .result + ) + filters = [raillabel.filter.ExcludeObjectTypeFilter(["train"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(object_name="person_0001").result + + +def test_include_sensor_ids(): + scene = ( + SceneBuilder.empty().add_bbox(sensor_id="rgb_middle").add_cuboid(sensor_id="lidar").result + ) + filters = [raillabel.filter.IncludeSensorIdFilter(["rgb_middle"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(sensor_id="rgb_middle").result + + +def test_exclude_sensor_ids(): + scene = ( + SceneBuilder.empty().add_bbox(sensor_id="rgb_middle").add_cuboid(sensor_id="lidar").result + ) + filters = [raillabel.filter.ExcludeSensorIdFilter(["lidar"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(sensor_id="rgb_middle").result + + +def test_include_sensor_types(): + scene = ( + SceneBuilder.empty() + .add_bbox(sensor_id="rgb_middle") + .add_cuboid(sensor_id="lidar_middle") + .result + ) + filters = [raillabel.filter.IncludeSensorTypeFilter(["camera"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(sensor_id="rgb_middle").result + + +def test_exclude_sensor_types(): + scene = ( + SceneBuilder.empty() + .add_bbox(sensor_id="rgb_middle") + .add_cuboid(sensor_id="lidar_middle") + .result + ) + filters = [raillabel.filter.ExcludeSensorTypeFilter(["lidar"])] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(sensor_id="rgb_middle").result + + +def test_include_attributes__only_keys(): + scene = ( + SceneBuilder.empty() + .add_bbox(attributes={"length": 42}) + .add_bbox(attributes={"width": 34}) + .result + ) + filters = [raillabel.filter.IncludeAttributesFilter({"length": None})] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(attributes={"length": 42}).result + + +def test_include_attributes__with_values(): + scene = ( + SceneBuilder.empty() + .add_bbox(attributes={"is_dummy": True}) + .add_bbox(attributes={"is_dummy": False}) + .result + ) + filters = [raillabel.filter.IncludeAttributesFilter({"is_dummy": True})] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(attributes={"is_dummy": True}).result + + +def test_exclude_attributes__only_keys(): + scene = ( + SceneBuilder.empty() + .add_bbox(attributes={"length": 42}) + .add_bbox(attributes={"width": 34}) + .result + ) + filters = [raillabel.filter.ExcludeAttributesFilter({"width": None})] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(attributes={"length": 42}).result + + +def test_exclude_attributes__with_values(): + scene = ( + SceneBuilder.empty() + .add_bbox(attributes={"is_dummy": True}) + .add_bbox(attributes={"is_dummy": False}) + .result + ) + filters = [raillabel.filter.ExcludeAttributesFilter({"is_dummy": False})] + + actual = scene.filter(filters) + assert actual == SceneBuilder.empty().add_bbox(attributes={"is_dummy": True}).result + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/raillabel/_util/__init__.py b/tests/format/__init__.py similarity index 100% rename from raillabel/_util/__init__.py rename to tests/format/__init__.py diff --git a/tests/format/test_attributes.py b/tests/format/test_attributes.py new file mode 100644 index 0000000..604652e --- /dev/null +++ b/tests/format/test_attributes.py @@ -0,0 +1,97 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.json_format import ( + JSONAttributes, + JSONBooleanAttribute, + JSONNumAttribute, + JSONTextAttribute, + JSONVecAttribute, +) +from raillabel.format._attributes import ( + _attributes_from_json, + _attributes_to_json, + UnsupportedAttributeTypeError, +) + +# == Fixtures ========================= + + +@pytest.fixture +def attributes_multiple_types_json() -> JSONAttributes: + return JSONAttributes( + boolean=[ + JSONBooleanAttribute(name="has_red_hat", val=True), + JSONBooleanAttribute(name="has_green_hat", val=False), + ], + num=[JSONNumAttribute(name="number_of_red_clothing_items", val=2)], + text=[JSONTextAttribute(name="color_of_hat", val="red")], + vec=[ + JSONVecAttribute( + name="clothing_items", val=["red_hat", "brown_coat", "black_pants", "red_shoes"] + ) + ], + ) + + +@pytest.fixture +def attributes_multiple_types() -> dict: + return { + "has_red_hat": True, + "has_green_hat": False, + "number_of_red_clothing_items": 2, + "color_of_hat": "red", + "clothing_items": ["red_hat", "brown_coat", "black_pants", "red_shoes"], + } + + +# == Tests ============================ + + +def test_attributes_from_json__none(): + actual = _attributes_from_json(None) + assert actual == {} + + +def test_attributes_from_json__empty(): + json_attributes = JSONAttributes( + boolean=None, + num=None, + text=None, + vec=None, + ) + + actual = _attributes_from_json(json_attributes) + assert actual == {} + + +def test_attributes_from_json__multiple_types( + attributes_multiple_types, attributes_multiple_types_json +): + actual = _attributes_from_json(attributes_multiple_types_json) + assert actual == attributes_multiple_types + + +def test_attributes_to_json__empty(): + actual = _attributes_to_json({}) + assert actual == None + + +def test_attributes_to_json__multiple_types( + attributes_multiple_types, attributes_multiple_types_json +): + actual = _attributes_to_json(attributes_multiple_types) + assert actual == attributes_multiple_types_json + + +def test_attributes_to_json__unsupported_type(): + with pytest.raises(UnsupportedAttributeTypeError): + _attributes_to_json({"attribute_with_unsupported_type": object}) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_bbox.py b/tests/format/test_bbox.py new file mode 100644 index 0000000..7c061de --- /dev/null +++ b/tests/format/test_bbox.py @@ -0,0 +1,71 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from raillabel.format import Bbox +from raillabel.json_format import JSONBbox + +# == Fixtures ========================= + + +@pytest.fixture +def bbox_json( + attributes_multiple_types_json, + point2d_json, + size2d_json, +) -> JSONBbox: + return JSONBbox( + uid="2811f67c-124C-4fac-a275-20807d0471de", + name="rgb_middle__bbox__person", + val=point2d_json + size2d_json, + coordinate_system="rgb_middle", + attributes=attributes_multiple_types_json, + ) + + +@pytest.fixture +def bbox_id() -> UUID: + return UUID("2811f67c-124C-4fac-a275-20807d0471de") + + +@pytest.fixture +def bbox( + point2d, + size2d, + attributes_multiple_types, + object_person_id, +) -> Bbox: + return Bbox( + pos=point2d, + size=size2d, + sensor_id="rgb_middle", + attributes=attributes_multiple_types, + object_id=object_person_id, + ) + + +# == Tests ============================ + + +def test_from_json(bbox, bbox_json, object_person_id): + actual = Bbox.from_json(bbox_json, object_person_id) + assert actual == bbox + + +def test_name(bbox): + actual = bbox.name("person") + assert actual == "rgb_middle__bbox__person" + + +def test_to_json(bbox, bbox_json, bbox_id): + actual = bbox.to_json(bbox_id, object_type="person") + assert actual == bbox_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_camera.py b/tests/format/test_camera.py new file mode 100644 index 0000000..58598f0 --- /dev/null +++ b/tests/format/test_camera.py @@ -0,0 +1,72 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Camera, IntrinsicsPinhole +from raillabel.json_format import ( + JSONCoordinateSystem, + JSONStreamCamera, + JSONStreamCameraProperties, + JSONIntrinsicsPinhole, +) + +# == Fixtures ========================= + + +@pytest.fixture +def camera_json( + intrinsics_pinhole_json, transform_json +) -> tuple[JSONStreamCamera, JSONCoordinateSystem]: + return ( + JSONStreamCamera( + type="camera", + stream_properties=JSONStreamCameraProperties(intrinsics_pinhole=intrinsics_pinhole_json), + uri="/path/to/sensor/data", + description="A very nice camera", + ), + JSONCoordinateSystem( + parent="base", type="sensor", pose_wrt_parent=transform_json, children=None + ), + ) + + +@pytest.fixture +def camera(intrinsics_pinhole, transform) -> Camera: + return Camera( + intrinsics=intrinsics_pinhole, + extrinsics=transform, + uri="/path/to/sensor/data", + description="A very nice camera", + ) + + +@pytest.fixture +def camera_empty() -> Camera: + return Camera( + intrinsics=IntrinsicsPinhole( + camera_matrix=tuple([0] * 12), + distortion=tuple([0] * 5), + width_px=0, + height_px=0, + ) + ) + + +# == Tests ============================ + + +def test_from_json(camera, camera_json): + actual = Camera.from_json(camera_json[0], camera_json[1]) + assert actual == camera + + +def test_to_json(camera, camera_json): + actual = camera.to_json() + assert actual == camera_json + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/format/test_cuboid.py b/tests/format/test_cuboid.py new file mode 100644 index 0000000..b71d46f --- /dev/null +++ b/tests/format/test_cuboid.py @@ -0,0 +1,74 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from raillabel.format import Cuboid +from raillabel.json_format import JSONCuboid + +# == Fixtures ========================= + + +@pytest.fixture +def cuboid_json( + attributes_multiple_types_json, + point3d_json, + quaternion_json, + size3d_json, +) -> JSONCuboid: + return JSONCuboid( + uid="51def938-20BA-4699-95be-d6330c44cb77", + name="lidar__cuboid__person", + val=point3d_json + quaternion_json + size3d_json, + coordinate_system="lidar", + attributes=attributes_multiple_types_json, + ) + + +@pytest.fixture +def cuboid_id() -> UUID: + return UUID("51def938-20BA-4699-95be-d6330c44cb77") + + +@pytest.fixture +def cuboid( + point3d, + size3d, + quaternion, + attributes_multiple_types, + object_person_id, +) -> Cuboid: + return Cuboid( + pos=point3d, + quat=quaternion, + size=size3d, + sensor_id="lidar", + attributes=attributes_multiple_types, + object_id=object_person_id, + ) + + +# == Tests ============================ + + +def test_from_json(cuboid, cuboid_json, object_person_id): + actual = Cuboid.from_json(cuboid_json, object_person_id) + assert actual == cuboid + + +def test_name(cuboid): + actual = cuboid.name("person") + assert actual == "lidar__cuboid__person" + + +def test_to_json(cuboid, cuboid_json, cuboid_id): + actual = cuboid.to_json(cuboid_id, object_type="person") + assert actual == cuboid_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_frame.py b/tests/format/test_frame.py new file mode 100644 index 0000000..1b85ba6 --- /dev/null +++ b/tests/format/test_frame.py @@ -0,0 +1,107 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from decimal import Decimal + +import pytest + +from raillabel.format import Frame +from raillabel.json_format import ( + JSONFrame, + JSONFrameData, + JSONFrameProperties, + JSONObjectData, + JSONAnnotations, +) + +# == Fixtures ========================= + + +@pytest.fixture +def frame_json( + sensor_reference_json, + another_sensor_reference_json, + num_json, + bbox_json, + cuboid_json, + poly2d_json, + poly3d_json, + seg3d_json, +) -> JSONFrame: + return JSONFrame( + frame_properties=JSONFrameProperties( + timestamp=Decimal("1631337747.123123123"), + streams={ + "rgb_middle": sensor_reference_json, + "lidar": another_sensor_reference_json, + }, + frame_data=JSONFrameData(num=[num_json]), + ), + objects={ + "cfcf9750-3bc3-4077-9079-a82c0c63976a": JSONObjectData( + object_data=JSONAnnotations( + poly2d=[poly2d_json], + poly3d=[poly3d_json], + ) + ), + "b40ba3ad-0327-46ff-9c28-2506cfd6d934": JSONObjectData( + object_data=JSONAnnotations( + bbox=[bbox_json], + cuboid=[cuboid_json], + vec=[seg3d_json], + ) + ), + }, + ) + + +@pytest.fixture +def frame( + sensor_reference, + another_sensor_reference, + num, + bbox, + bbox_id, + cuboid, + cuboid_id, + poly2d, + poly2d_id, + poly3d, + poly3d_id, + seg3d, + seg3d_id, +) -> dict: + return Frame( + timestamp=Decimal("1631337747.123123123"), + sensors={ + "rgb_middle": sensor_reference, + "lidar": another_sensor_reference, + }, + frame_data={num.name: num}, + annotations={ + bbox_id: bbox, + cuboid_id: cuboid, + poly2d_id: poly2d, + poly3d_id: poly3d, + seg3d_id: seg3d, + }, + ) + + +# == Tests ============================ + + +def test_from_json(frame, frame_json): + actual = Frame.from_json(frame_json) + assert actual == frame + + +def test_to_json(frame, frame_json, objects): + actual = frame.to_json(objects) + assert actual == frame_json + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/format/test_frame_interval.py b/tests/format/test_frame_interval.py new file mode 100644 index 0000000..7633ec7 --- /dev/null +++ b/tests/format/test_frame_interval.py @@ -0,0 +1,88 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import FrameInterval +from raillabel.json_format import JSONFrameInterval + +# == Fixtures ========================= + + +@pytest.fixture +def frame_interval_json() -> JSONFrameInterval: + return JSONFrameInterval(frame_start=12, frame_end=16) + + +@pytest.fixture +def frame_interval() -> dict: + return FrameInterval( + start=12, + end=16, + ) + + +# == Tests ============================ + + +def test_from_json(frame_interval, frame_interval_json): + actual = FrameInterval.from_json(frame_interval_json) + assert actual == frame_interval + + +def test_len(): + frame_interval = FrameInterval( + start=12, + end=16, + ) + + assert len(frame_interval) == 5 + + +def test_from_frame_ids_empty(): + frame_ids = [] + + assert FrameInterval.from_frame_ids(frame_ids) == [] + + +def test_from_frame_ids_one_frame(): + frame_ids = [1] + + assert FrameInterval.from_frame_ids(frame_ids) == [FrameInterval(1, 1)] + + +def test_from_frame_ids_one_interval(): + frame_ids = [1, 2, 3, 4] + + assert FrameInterval.from_frame_ids(frame_ids) == [FrameInterval(1, 4)] + + +def test_from_frame_ids_multiple_intervals(): + frame_ids = [0, 1, 2, 3, 6, 7, 9, 12, 13, 14] + + assert FrameInterval.from_frame_ids(frame_ids) == [ + FrameInterval(0, 3), + FrameInterval(6, 7), + FrameInterval(9, 9), + FrameInterval(12, 14), + ] + + +def test_from_frame_ids_unsorted(): + frame_ids = [5, 2, 1, 3] + + assert FrameInterval.from_frame_ids(frame_ids) == [ + FrameInterval(1, 3), + FrameInterval(5, 5), + ] + + +def test_to_json(frame_interval, frame_interval_json): + actual = frame_interval.to_json() + assert actual == frame_interval_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_gps_imu.py b/tests/format/test_gps_imu.py new file mode 100644 index 0000000..6bc33e1 --- /dev/null +++ b/tests/format/test_gps_imu.py @@ -0,0 +1,51 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import GpsImu +from raillabel.json_format import JSONCoordinateSystem, JSONStreamOther + +# == Fixtures ========================= + + +@pytest.fixture +def gps_imu_json(transform_json) -> tuple[JSONStreamOther, JSONCoordinateSystem]: + return ( + JSONStreamOther( + type="gps_imu", + uri="/path/to/sensor/data", + description="A very nice gps_imu", + ), + JSONCoordinateSystem( + parent="base", type="sensor", pose_wrt_parent=transform_json, children=None + ), + ) + + +@pytest.fixture +def gps_imu(transform) -> dict: + return GpsImu( + extrinsics=transform, + uri="/path/to/sensor/data", + description="A very nice gps_imu", + ) + + +# == Tests ============================ + + +def test_from_json(gps_imu, gps_imu_json): + actual = GpsImu.from_json(gps_imu_json[0], gps_imu_json[1]) + assert actual == gps_imu + + +def test_to_json(gps_imu, gps_imu_json): + actual = gps_imu.to_json() + assert actual == gps_imu_json + + +if __name__ == "__main__": + pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/format/test_intrinsics_pinhole.py b/tests/format/test_intrinsics_pinhole.py new file mode 100644 index 0000000..521a6e6 --- /dev/null +++ b/tests/format/test_intrinsics_pinhole.py @@ -0,0 +1,48 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import IntrinsicsPinhole +from raillabel.json_format import JSONIntrinsicsPinhole + +# == Fixtures ========================= + + +@pytest.fixture +def intrinsics_pinhole_json() -> dict: + return JSONIntrinsicsPinhole( + camera_matrix=[0.48, 0, 0.81, 0, 0, 0.16, 0.83, 0, 0, 0, 1, 0], + distortion_coeffs=[0.49, 0.69, 0.31, 0.81, 0.99], + width_px=2464, + height_px=1600, + ) + + +@pytest.fixture +def intrinsics_pinhole() -> dict: + return IntrinsicsPinhole( + camera_matrix=(0.48, 0, 0.81, 0, 0, 0.16, 0.83, 0, 0, 0, 1, 0), + distortion=(0.49, 0.69, 0.31, 0.81, 0.99), + width_px=2464, + height_px=1600, + ) + + +# == Tests ============================ + + +def test_from_json(intrinsics_pinhole, intrinsics_pinhole_json): + actual = IntrinsicsPinhole.from_json(intrinsics_pinhole_json) + assert actual == intrinsics_pinhole + + +def test_to_json(intrinsics_pinhole, intrinsics_pinhole_json): + actual = intrinsics_pinhole.to_json() + assert actual == intrinsics_pinhole_json + + +if __name__ == "__main__": + pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/format/test_intrinsics_radar.py b/tests/format/test_intrinsics_radar.py new file mode 100644 index 0000000..b0043b9 --- /dev/null +++ b/tests/format/test_intrinsics_radar.py @@ -0,0 +1,46 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import IntrinsicsRadar +from raillabel.json_format import JSONIntrinsicsRadar + +# == Fixtures ========================= + + +@pytest.fixture +def intrinsics_radar_json() -> dict: + return JSONIntrinsicsRadar( + resolution_px_per_m=2.856, + width_px=2856, + height_px=1428, + ) + + +@pytest.fixture +def intrinsics_radar() -> dict: + return IntrinsicsRadar( + resolution_px_per_m=2.856, + width_px=2856, + height_px=1428, + ) + + +# == Tests ============================ + + +def test_from_json(intrinsics_radar, intrinsics_radar_json): + actual = IntrinsicsRadar.from_json(intrinsics_radar_json) + assert actual == intrinsics_radar + + +def test_to_json(intrinsics_radar, intrinsics_radar_json): + actual = intrinsics_radar.to_json() + assert actual == intrinsics_radar_json + + +if __name__ == "__main__": + pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/format/test_lidar.py b/tests/format/test_lidar.py new file mode 100644 index 0000000..a4d151d --- /dev/null +++ b/tests/format/test_lidar.py @@ -0,0 +1,51 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Lidar +from raillabel.json_format import JSONCoordinateSystem, JSONStreamOther + +# == Fixtures ========================= + + +@pytest.fixture +def lidar_json(transform_json) -> tuple[JSONStreamOther, JSONCoordinateSystem]: + return ( + JSONStreamOther( + type="lidar", + uri="/path/to/sensor/data", + description="A very nice lidar", + ), + JSONCoordinateSystem( + parent="base", type="sensor", pose_wrt_parent=transform_json, children=None + ), + ) + + +@pytest.fixture +def lidar(transform) -> dict: + return Lidar( + extrinsics=transform, + uri="/path/to/sensor/data", + description="A very nice lidar", + ) + + +# == Tests ============================ + + +def test_from_json(lidar, lidar_json): + actual = Lidar.from_json(lidar_json[0], lidar_json[1]) + assert actual == lidar + + +def test_to_json(lidar, lidar_json): + actual = lidar.to_json() + assert actual == lidar_json + + +if __name__ == "__main__": + pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/format/test_metadata.py b/tests/format/test_metadata.py new file mode 100644 index 0000000..35494ac --- /dev/null +++ b/tests/format/test_metadata.py @@ -0,0 +1,76 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations +import json + +import pytest + +from raillabel.format import Metadata +from raillabel.json_format import JSONMetadata + +# == Fixtures ========================= + + +@pytest.fixture +def metadata_json() -> JSONMetadata: + return JSONMetadata( + schema_version="1.0.0", + name="some_file", + subschema_version="4.0.0", + exporter_version="1.2.3", + file_version="0.1.5", + tagged_file="path/to/data", + annotator="John Doe", + comment="this is a very nice annotation file", + ) + + +@pytest.fixture +def metadata() -> dict: + return Metadata( + schema_version="1.0.0", + name="some_file", + subschema_version="4.0.0", + exporter_version="1.2.3", + file_version="0.1.5", + tagged_file="path/to/data", + annotator="John Doe", + comment="this is a very nice annotation file", + ) + + +# == Tests ============================ + + +def test_from_json(metadata, metadata_json): + actual = Metadata.from_json(metadata_json) + assert actual == metadata + + +def test_to_json(metadata, metadata_json): + actual = metadata.to_json() + assert actual == metadata_json + + +def test_extra_fields(): + json_metadata = JSONMetadata( + **{ + "schema_version": "1.0.0", + "ADDITIONAL_STR": "SOME_VALUE", + "ADDITIONAL_OBJECT": {"first_field": 2, "second_field": [1, 2, 3]}, + } + ) + metadata = Metadata.from_json(json_metadata) + json_metadata_again = metadata.to_json() + + actual = json.loads(json_metadata_again.model_dump_json(exclude_none=True)) + assert actual == { + "schema_version": "1.0.0", + "ADDITIONAL_STR": "SOME_VALUE", + "ADDITIONAL_OBJECT": {"first_field": 2, "second_field": [1, 2, 3]}, + } + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_num.py b/tests/format/test_num.py new file mode 100644 index 0000000..2779c86 --- /dev/null +++ b/tests/format/test_num.py @@ -0,0 +1,55 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from raillabel.format import Num +from raillabel.json_format import JSONNum + +# == Fixtures ========================= + + +@pytest.fixture +def num_json(num_id) -> JSONNum: + return JSONNum( + uid=num_id, + name="velocity", + val=49.21321, + coordinate_system="gps_imu", + ) + + +@pytest.fixture +def num_id() -> UUID: + return UUID("78f0ad89-2750-4a30-9d66-44c9da73a714") + + +@pytest.fixture +def num(num_id) -> Num: + return Num( + sensor_id="gps_imu", + name="velocity", + val=49.21321, + id=num_id, + ) + + +# == Tests ============================ + + +def test_from_json(num, num_json): + actual = Num.from_json(num_json) + assert actual == num + + +def test_to_json(num, num_json): + actual = num.to_json() + assert actual == num_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_object.py b/tests/format/test_object.py new file mode 100644 index 0000000..a7b69a1 --- /dev/null +++ b/tests/format/test_object.py @@ -0,0 +1,151 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from raillabel.json_format import JSONObject, JSONFrameInterval, JSONElementDataPointer +from raillabel.format import Object + +# == Fixtures ========================= + + +@pytest.fixture +def objects(object_person, object_person_id, object_track, object_track_id) -> dict[UUID, Object]: + return { + object_person_id: object_person, + object_track_id: object_track, + } + + +@pytest.fixture +def object_person_json() -> JSONObject: + return JSONObject( + name="person_0032", + type="person", + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + object_data_pointers={ + "rgb_middle__bbox__person": JSONElementDataPointer( + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + type="bbox", + attribute_pointers={ + "has_red_hat": "boolean", + "has_green_hat": "boolean", + "number_of_red_clothing_items": "num", + "color_of_hat": "text", + "clothing_items": "vec", + }, + ), + "lidar__cuboid__person": JSONElementDataPointer( + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + type="cuboid", + attribute_pointers={ + "has_red_hat": "boolean", + "has_green_hat": "boolean", + "number_of_red_clothing_items": "num", + "color_of_hat": "text", + "clothing_items": "vec", + }, + ), + "lidar__vec__person": JSONElementDataPointer( + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + type="vec", + attribute_pointers={ + "has_red_hat": "boolean", + "has_green_hat": "boolean", + "number_of_red_clothing_items": "num", + "color_of_hat": "text", + "clothing_items": "vec", + }, + ), + }, + ) + + +@pytest.fixture +def object_person() -> Object: + return Object( + name="person_0032", + type="person", + ) + + +@pytest.fixture +def object_person_id() -> UUID: + return UUID("b40ba3ad-0327-46ff-9c28-2506cfd6d934") + + +@pytest.fixture +def object_track_json() -> JSONObject: + return JSONObject( + name="track_0001", + type="track", + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + object_data_pointers={ + "rgb_middle__poly2d__track": JSONElementDataPointer( + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + type="poly2d", + attribute_pointers={ + "has_red_hat": "boolean", + "has_green_hat": "boolean", + "number_of_red_clothing_items": "num", + "color_of_hat": "text", + "clothing_items": "vec", + }, + ), + "lidar__poly3d__track": JSONElementDataPointer( + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + type="poly3d", + attribute_pointers={ + "has_red_hat": "boolean", + "has_green_hat": "boolean", + "number_of_red_clothing_items": "num", + "color_of_hat": "text", + "clothing_items": "vec", + }, + ), + }, + ) + + +@pytest.fixture +def object_track() -> Object: + return Object( + name="track_0001", + type="track", + ) + + +@pytest.fixture +def object_track_id() -> UUID: + return UUID("cfcf9750-3BC3-4077-9079-a82c0c63976a") + + +# == Tests ============================ + + +def test_from_json__person(object_person, object_person_json): + actual = Object.from_json(object_person_json) + assert actual == object_person + + +def test_from_json__track(object_track, object_track_json): + actual = Object.from_json(object_track_json) + assert actual == object_track + + +def test_to_json__person(object_person, object_person_json, object_person_id, frame): + actual = object_person.to_json(object_person_id, {1: frame}) + assert actual == object_person_json + + +def test_to_json__track(object_track, object_track_json, object_track_id, frame): + actual = object_track.to_json(object_track_id, {1: frame}) + assert actual == object_track_json + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/format/test_other_sensor.py b/tests/format/test_other_sensor.py new file mode 100644 index 0000000..2cb693a --- /dev/null +++ b/tests/format/test_other_sensor.py @@ -0,0 +1,51 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import OtherSensor +from raillabel.json_format import JSONCoordinateSystem, JSONStreamOther + +# == Fixtures ========================= + + +@pytest.fixture +def other_json(transform_json) -> tuple[JSONStreamOther, JSONCoordinateSystem]: + return ( + JSONStreamOther( + type="other", + uri="/path/to/sensor/data", + description="A very nice generic sensor", + ), + JSONCoordinateSystem( + parent="base", type="sensor", pose_wrt_parent=transform_json, children=None + ), + ) + + +@pytest.fixture +def other(transform) -> dict: + return OtherSensor( + extrinsics=transform, + uri="/path/to/sensor/data", + description="A very nice generic sensor", + ) + + +# == Tests ============================ + + +def test_from_json(other, other_json): + actual = OtherSensor.from_json(other_json[0], other_json[1]) + assert actual == other + + +def test_to_json(other, other_json): + actual = other.to_json() + assert actual == other_json + + +if __name__ == "__main__": + pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/format/test_point2d.py b/tests/format/test_point2d.py new file mode 100644 index 0000000..54d3302 --- /dev/null +++ b/tests/format/test_point2d.py @@ -0,0 +1,57 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Point2d + +# == Fixtures ========================= + + +@pytest.fixture +def point2d_json() -> tuple[float, float]: + return [1.5, 222] + + +@pytest.fixture +def point2d() -> Point2d: + return Point2d(1.5, 222) + + +@pytest.fixture +def another_point2d_json() -> tuple[float, float]: + return [1.7, 222.2] + + +@pytest.fixture +def another_point2d() -> Point2d: + return Point2d(1.7, 222.2) + + +# == Tests ============================ + + +def test_from_json(point2d, point2d_json): + actual = Point2d.from_json(point2d_json) + assert actual == point2d + + +def test_from_json__another(another_point2d, another_point2d_json): + actual = Point2d.from_json(another_point2d_json) + assert actual == another_point2d + + +def test_to_json(point2d, point2d_json): + actual = point2d.to_json() + assert actual == tuple(point2d_json) + + +def test_to_json__another(another_point2d, another_point2d_json): + actual = another_point2d.to_json() + assert actual == tuple(another_point2d_json) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_point3d.py b/tests/format/test_point3d.py new file mode 100644 index 0000000..1fef803 --- /dev/null +++ b/tests/format/test_point3d.py @@ -0,0 +1,57 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Point3d + +# == Fixtures ========================= + + +@pytest.fixture +def point3d_json() -> dict: + return [419, 3.14, 0] + + +@pytest.fixture +def point3d() -> dict: + return Point3d(419, 3.14, 0) + + +@pytest.fixture +def another_point3d_json() -> dict: + return [419.2, 3.34, 0.2] + + +@pytest.fixture +def another_point3d() -> dict: + return Point3d(419.2, 3.34, 0.2) + + +# == Tests ============================ + + +def test_from_json(point3d, point3d_json): + actual = Point3d.from_json(point3d_json) + assert actual == point3d + + +def test_from_json__another(another_point3d, another_point3d_json): + actual = Point3d.from_json(another_point3d_json) + assert actual == another_point3d + + +def test_to_json(point3d, point3d_json): + actual = point3d.to_json() + assert actual == tuple(point3d_json) + + +def test_to_json__another(another_point3d, another_point3d_json): + actual = another_point3d.to_json() + assert actual == tuple(another_point3d_json) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_poly2d.py b/tests/format/test_poly2d.py new file mode 100644 index 0000000..c193197 --- /dev/null +++ b/tests/format/test_poly2d.py @@ -0,0 +1,73 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from raillabel.format import Poly2d +from raillabel.json_format import JSONPoly2d + +# == Fixtures ========================= + + +@pytest.fixture +def poly2d_json( + point2d_json, + another_point2d_json, + attributes_multiple_types_json, +) -> JSONPoly2d: + return JSONPoly2d( + uid="013e7b34-62E5-435c-9412-87318c50f6d8", + name="rgb_middle__poly2d__track", + closed=True, + mode="MODE_POLY2D_ABSOLUTE", + val=point2d_json + another_point2d_json, + coordinate_system="rgb_middle", + attributes=attributes_multiple_types_json, + ) + + +@pytest.fixture +def poly2d_id() -> UUID: + return UUID("013e7b34-62E5-435c-9412-87318c50f6d8") + + +@pytest.fixture +def poly2d( + point2d, + another_point2d, + attributes_multiple_types, + object_track_id, +) -> Poly2d: + return Poly2d( + points=[point2d, another_point2d], + closed=True, + sensor_id="rgb_middle", + attributes=attributes_multiple_types, + object_id=object_track_id, + ) + + +# == Tests ============================ + + +def test_from_json(poly2d, poly2d_json, object_track_id): + actual = Poly2d.from_json(poly2d_json, object_track_id) + assert actual == poly2d + + +def test_name(poly2d): + actual = poly2d.name("track") + assert actual == "rgb_middle__poly2d__track" + + +def test_to_json(poly2d, poly2d_json, poly2d_id): + actual = poly2d.to_json(poly2d_id, object_type="track") + assert actual == poly2d_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_poly3d.py b/tests/format/test_poly3d.py new file mode 100644 index 0000000..e62d77e --- /dev/null +++ b/tests/format/test_poly3d.py @@ -0,0 +1,72 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from raillabel.format import Poly3d +from raillabel.json_format import JSONPoly3d + +# == Fixtures ========================= + + +@pytest.fixture +def poly3d_json( + point3d_json, + another_point3d_json, + attributes_multiple_types_json, +) -> JSONPoly3d: + return JSONPoly3d( + uid="0da87210-46F1-40e5-b661-20ea1c392f50", + name="lidar__poly3d__track", + closed=True, + val=point3d_json + another_point3d_json, + coordinate_system="lidar", + attributes=attributes_multiple_types_json, + ) + + +@pytest.fixture +def poly3d_id() -> UUID: + return UUID("0da87210-46F1-40e5-b661-20ea1c392f50") + + +@pytest.fixture +def poly3d( + point3d, + another_point3d, + attributes_multiple_types, + object_track_id, +) -> Poly3d: + return Poly3d( + points=[point3d, another_point3d], + closed=True, + sensor_id="lidar", + attributes=attributes_multiple_types, + object_id=object_track_id, + ) + + +# == Tests ============================ + + +def test_from_json(poly3d, poly3d_json, object_track_id): + actual = Poly3d.from_json(poly3d_json, object_track_id) + assert actual == poly3d + + +def test_name(poly3d): + actual = poly3d.name("track") + assert actual == "lidar__poly3d__track" + + +def test_to_json(poly3d, poly3d_json, poly3d_id): + actual = poly3d.to_json(poly3d_id, object_type="track") + assert actual == poly3d_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_quaternion.py b/tests/format/test_quaternion.py new file mode 100644 index 0000000..bff5656 --- /dev/null +++ b/tests/format/test_quaternion.py @@ -0,0 +1,37 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Quaternion + +# == Fixtures ========================= + + +@pytest.fixture +def quaternion_json() -> dict: + return [0.75318325, -0.10270147, 0.21430262, -0.61338551] + + +@pytest.fixture +def quaternion() -> dict: + return Quaternion(0.75318325, -0.10270147, 0.21430262, -0.61338551) + + +# == Tests ============================ + + +def test_from_json(quaternion, quaternion_json): + actual = Quaternion.from_json(quaternion_json) + assert actual == quaternion + + +def test_to_json(quaternion, quaternion_json): + actual = quaternion.to_json() + assert actual == tuple(quaternion_json) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_radar.py b/tests/format/test_radar.py new file mode 100644 index 0000000..2e15f59 --- /dev/null +++ b/tests/format/test_radar.py @@ -0,0 +1,71 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Radar, IntrinsicsRadar +from raillabel.json_format import ( + JSONCoordinateSystem, + JSONStreamRadar, + JSONStreamRadarProperties, + JSONIntrinsicsRadar, +) + +# == Fixtures ========================= + + +@pytest.fixture +def radar_json( + intrinsics_radar_json, transform_json +) -> tuple[JSONStreamRadar, JSONCoordinateSystem]: + return ( + JSONStreamRadar( + type="radar", + stream_properties=JSONStreamRadarProperties(intrinsics_radar=intrinsics_radar_json), + uri="/path/to/sensor/data", + description="A very nice radar", + ), + JSONCoordinateSystem( + parent="base", type="sensor", pose_wrt_parent=transform_json, children=None + ), + ) + + +@pytest.fixture +def radar(intrinsics_radar, transform) -> Radar: + return Radar( + intrinsics=intrinsics_radar, + extrinsics=transform, + uri="/path/to/sensor/data", + description="A very nice radar", + ) + + +@pytest.fixture +def radar_empty() -> Radar: + return Radar( + intrinsics=IntrinsicsRadar( + resolution_px_per_m=0, + width_px=0, + height_px=0, + ) + ) + + +# == Tests ============================ + + +def test_from_json(radar, radar_json): + actual = Radar.from_json(radar_json[0], radar_json[1]) + assert actual == radar + + +def test_to_json(radar, radar_json): + actual = radar.to_json() + assert actual == radar_json + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/format/test_scene.py b/tests/format/test_scene.py new file mode 100644 index 0000000..b7dbe24 --- /dev/null +++ b/tests/format/test_scene.py @@ -0,0 +1,101 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Scene +from raillabel.json_format import ( + JSONScene, + JSONSceneContent, + JSONCoordinateSystem, + JSONFrameInterval, +) + +# == Fixtures ========================= + + +@pytest.fixture +def scene_json( + metadata_json, + camera_json, + lidar_json, + radar_json, + object_person_id, + object_person_json, + object_track_id, + object_track_json, + frame_json, +) -> JSONScene: + return JSONScene( + openlabel=JSONSceneContent( + metadata=metadata_json, + coordinate_systems={ + "base": JSONCoordinateSystem( + parent="", + type="local", + pose_wrt_parent=None, + children=["rgb_middle", "lidar", "radar"], + ), + "rgb_middle": camera_json[1], + "lidar": lidar_json[1], + "radar": radar_json[1], + }, + streams={ + "rgb_middle": camera_json[0], + "lidar": lidar_json[0], + "radar": radar_json[0], + }, + objects={ + object_person_id: object_person_json, + object_track_id: object_track_json, + }, + frames={1: frame_json}, + frame_intervals=[JSONFrameInterval(frame_start=1, frame_end=1)], + ) + ) + + +@pytest.fixture +def scene( + metadata, + camera, + lidar, + radar, + object_person_id, + object_person, + object_track_id, + object_track, + frame, +) -> Scene: + return Scene( + metadata=metadata, + sensors={ + "rgb_middle": camera, + "lidar": lidar, + "radar": radar, + }, + objects={ + object_person_id: object_person, + object_track_id: object_track, + }, + frames={1: frame}, + ) + + +# == Tests ============================ + + +def test_from_json(scene, scene_json): + actual = Scene.from_json(scene_json) + assert actual == scene + + +def test_to_json(scene, scene_json): + actual = scene.to_json() + assert actual == scene_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_seg3d.py b/tests/format/test_seg3d.py new file mode 100644 index 0000000..a290aa6 --- /dev/null +++ b/tests/format/test_seg3d.py @@ -0,0 +1,66 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from raillabel.format import Seg3d +from raillabel.json_format import JSONVec + +# == Fixtures ========================= + + +@pytest.fixture +def seg3d_json( + attributes_multiple_types_json, +) -> JSONVec: + return JSONVec( + uid="d52e2b25-0B48-4899-86d5-4bc41be6b7d3", + name="lidar__vec__person", + val=[1234, 5678], + coordinate_system="lidar", + attributes=attributes_multiple_types_json, + ) + + +@pytest.fixture +def seg3d_id() -> UUID: + return UUID("d52e2b25-0B48-4899-86d5-4bc41be6b7d3") + + +@pytest.fixture +def seg3d( + attributes_multiple_types, + object_person_id, +) -> Seg3d: + return Seg3d( + point_ids=[1234, 5678], + sensor_id="lidar", + attributes=attributes_multiple_types, + object_id=object_person_id, + ) + + +# == Tests ============================ + + +def test_from_json(seg3d, seg3d_json, object_person_id): + actual = Seg3d.from_json(seg3d_json, object_person_id) + assert actual == seg3d + + +def test_name(seg3d): + actual = seg3d.name("person") + assert actual == "lidar__vec__person" + + +def test_to_json(seg3d, seg3d_json, seg3d_id): + actual = seg3d.to_json(seg3d_id, object_type="person") + assert actual == seg3d_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_sensor_reference.py b/tests/format/test_sensor_reference.py new file mode 100644 index 0000000..95ed9d3 --- /dev/null +++ b/tests/format/test_sensor_reference.py @@ -0,0 +1,69 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest +from decimal import Decimal + +from raillabel.format import SensorReference +from raillabel.json_format import JSONStreamSync, JSONStreamSyncProperties, JSONStreamSyncTimestamp + +# == Fixtures ========================= + + +@pytest.fixture +def sensor_reference_json() -> JSONStreamSync: + return JSONStreamSync( + stream_properties=JSONStreamSyncProperties( + sync=JSONStreamSyncTimestamp(timestamp="1631337747.123123123") + ), + uri="/uri/to/file.png", + ) + + +@pytest.fixture +def sensor_reference() -> SensorReference: + return SensorReference(timestamp=Decimal("1631337747.123123123"), uri="/uri/to/file.png") + + +@pytest.fixture +def another_sensor_reference_json() -> JSONStreamSync: + return JSONStreamSync( + stream_properties=JSONStreamSyncProperties( + sync=JSONStreamSyncTimestamp(timestamp="1631337747.103123123") + ), + uri="/uri/to/file.pcd", + ) + + +@pytest.fixture +def another_sensor_reference() -> SensorReference: + return SensorReference(timestamp=Decimal("1631337747.103123123"), uri="/uri/to/file.pcd") + + +# == Tests ============================ + + +def test_from_json(sensor_reference, sensor_reference_json): + actual = SensorReference.from_json(sensor_reference_json) + assert actual == sensor_reference + + +def test_from_json__another(another_sensor_reference, another_sensor_reference_json): + actual = SensorReference.from_json(another_sensor_reference_json) + assert actual == another_sensor_reference + + +def test_to_json(sensor_reference, sensor_reference_json): + actual = sensor_reference.to_json() + assert actual == sensor_reference_json + + +def test_to_json__another(another_sensor_reference, another_sensor_reference_json): + actual = another_sensor_reference.to_json() + assert actual == another_sensor_reference_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_size2d.py b/tests/format/test_size2d.py new file mode 100644 index 0000000..3df8424 --- /dev/null +++ b/tests/format/test_size2d.py @@ -0,0 +1,37 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Size2d + +# == Fixtures ========================= + + +@pytest.fixture +def size2d_json() -> dict: + return [25, 1.344] + + +@pytest.fixture +def size2d() -> dict: + return Size2d(25, 1.344) + + +# == Tests ============================ + + +def test_from_json(size2d, size2d_json): + actual = Size2d.from_json(size2d_json) + assert actual == size2d + + +def test_to_json(size2d, size2d_json): + actual = size2d.to_json() + assert actual == tuple(size2d_json) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_size3d.py b/tests/format/test_size3d.py new file mode 100644 index 0000000..801c4fa --- /dev/null +++ b/tests/format/test_size3d.py @@ -0,0 +1,37 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Size3d + +# == Fixtures ========================= + + +@pytest.fixture +def size3d_json() -> dict: + return [25, 1.344, 12.3] + + +@pytest.fixture +def size3d() -> dict: + return Size3d(25, 1.344, 12.3) + + +# == Tests ============================ + + +def test_from_json(size3d, size3d_json): + actual = Size3d.from_json(size3d_json) + assert actual == size3d + + +def test_to_json(size3d, size3d_json): + actual = size3d.to_json() + assert actual == tuple(size3d_json) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/format/test_transform.py b/tests/format/test_transform.py new file mode 100644 index 0000000..9f816dc --- /dev/null +++ b/tests/format/test_transform.py @@ -0,0 +1,39 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +from raillabel.format import Transform +from raillabel.json_format import JSONTransformData + + +# == Fixtures ========================= + + +@pytest.fixture +def transform_json(point3d_json, quaternion_json) -> JSONTransformData: + return JSONTransformData(translation=point3d_json, quaternion=quaternion_json) + + +@pytest.fixture +def transform(point3d, quaternion) -> Transform: + return Transform(pos=point3d, quat=quaternion) + + +# == Tests ============================ + + +def test_from_json(transform_json, transform): + actual = Transform.from_json(transform_json) + assert actual == transform + + +def test_to_json(transform, transform_json): + actual = transform.to_json() + assert actual == transform_json + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/json_format/test_scene.py b/tests/json_format/test_scene.py new file mode 100644 index 0000000..5229eb0 --- /dev/null +++ b/tests/json_format/test_scene.py @@ -0,0 +1,18 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +import os + +import pytest + +from raillabel.json_format import JSONScene + + +def test_parse__openlabel_short(json_data): + JSONScene(**json_data["openlabel_v1_short"]) + + +# Executes the test if the file is called +if __name__ == "__main__": + os.system("clear") + pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/load/test_load.py b/tests/load/test_load.py new file mode 100644 index 0000000..a80670a --- /dev/null +++ b/tests/load/test_load.py @@ -0,0 +1,19 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +import raillabel + + +def test_load(json_paths): + actual = raillabel.load(json_paths["openlabel_v1_short"]) + assert len(actual.sensors) == 4 + assert len(actual.objects) == 3 + assert len(actual.frames) == 2 + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/save/test_save.py b/tests/save/test_save.py new file mode 100644 index 0000000..4009a88 --- /dev/null +++ b/tests/save/test_save.py @@ -0,0 +1,23 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import pytest + +import raillabel +from raillabel import Scene +from raillabel.json_format import JSONScene + + +def test_save(json_data, tmp_path): + scene_path = tmp_path / "scene.json" + ground_truth_scene = Scene.from_json(JSONScene(**json_data["openlabel_v1_short"])) + raillabel.save(ground_truth_scene, scene_path) + + actual = raillabel.load(scene_path) + assert actual == ground_truth_scene + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/scene_builder/test_scene_builder.py b/tests/scene_builder/test_scene_builder.py new file mode 100644 index 0000000..d448aca --- /dev/null +++ b/tests/scene_builder/test_scene_builder.py @@ -0,0 +1,458 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations +from uuid import UUID +from decimal import Decimal + +import pytest + +import raillabel +from raillabel.scene_builder.scene_builder import SceneBuilder +from raillabel.format import ( + Scene, + Metadata, + Object, + Camera, + Lidar, + Radar, + GpsImu, + OtherSensor, + IntrinsicsPinhole, + IntrinsicsRadar, + Frame, + Bbox, + Point2d, + Size2d, + Cuboid, + Point3d, + Size3d, + Quaternion, + Poly2d, + Poly3d, + Seg3d, +) + + +def test_empty(): + actual = SceneBuilder.empty() + assert actual.result == Scene(Metadata(schema_version="1.0.0")) + + +def test_add_object__all_options(): + actual = SceneBuilder.empty().add_object( + object_id="5c59aad4-9fcd-4903-a9fa-72b1b76c23a5", + object_type="train", + object_name="train_0001", + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-9fcd-4903-a9fa-72b1b76c23a5"): Object(name="train_0001", type="train") + }, + ) + + +def test_add_object__no_object_name(): + actual = SceneBuilder.empty().add_object( + object_id="5c59aad4-9fcd-4903-a9fa-72b1b76c23a5", + object_type="train", + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-9fcd-4903-a9fa-72b1b76c23a5"): Object(name="train_0000", type="train") + }, + ) + + +def test_add_object__no_object_type(): + actual = SceneBuilder.empty().add_object( + object_id="5c59aad4-9fcd-4903-a9fa-72b1b76c23a5", + object_name="train_0000", + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-9fcd-4903-a9fa-72b1b76c23a5"): Object(name="train_0000", type="train") + }, + ) + + +def test_add_object__no_object_type_or_name(): + actual = SceneBuilder.empty().add_object( + object_id="5c59aad4-9fcd-4903-a9fa-72b1b76c23a5", + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-9fcd-4903-a9fa-72b1b76c23a5"): Object(name="person_0000", type="person") + }, + ) + + +def test_add_object__no_object_id(): + actual = SceneBuilder.empty().add_object(object_type="train", object_name="train_0001") + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="train_0001", type="train") + }, + ) + + +def test_add_object__object_id_iteration(): + actual = SceneBuilder.empty().add_object().add_object().add_object().add_object() + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0000", type="person"), + UUID("5c59aad4-0000-4000-0000-000000000001"): Object(name="person_0000", type="person"), + UUID("5c59aad4-0000-4000-0000-000000000002"): Object(name="person_0000", type="person"), + UUID("5c59aad4-0000-4000-0000-000000000003"): Object(name="person_0000", type="person"), + }, + ) + + +def test_add_sensor__camera_rgb(camera_empty): + actual = SceneBuilder.empty().add_sensor("rgb_middle") + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + sensors={"rgb_middle": camera_empty}, + ) + + +def test_add_sensor__camera_ir(camera_empty): + actual = SceneBuilder.empty().add_sensor("ir_left") + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + sensors={"ir_left": camera_empty}, + ) + + +def test_add_sensor__radar(radar_empty): + actual = SceneBuilder.empty().add_sensor("radar") + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + sensors={"radar": radar_empty}, + ) + + +def test_add_sensor__lidar(): + actual = SceneBuilder.empty().add_sensor("lidar") + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), sensors={"lidar": Lidar()} + ) + + +def test_add_sensor__gps_imu(): + actual = SceneBuilder.empty().add_sensor("gps_imu") + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), sensors={"gps_imu": GpsImu()} + ) + + +def test_add_sensor__other(): + actual = SceneBuilder.empty().add_sensor("SOMETHING_ELSE") + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), sensors={"SOMETHING_ELSE": OtherSensor()} + ) + + +def test_add_frame(): + actual = SceneBuilder.empty().add_frame(1, 1631691173) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), frames={1: Frame(timestamp=Decimal(1631691173))} + ) + + +def test_add_frame__no_timestamp(): + actual = SceneBuilder.empty().add_frame(1) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), frames={1: Frame(timestamp=None)} + ) + + +def test_add_frame__no_frame_id(): + actual = SceneBuilder.empty().add_frame().add_frame() + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + frames={ + 1: Frame(), + 2: Frame(), + }, + ) + + +def test_add_bbox(camera_empty): + actual = SceneBuilder.empty().add_bbox( + uid=UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"), + frame_id=2, + object_name="person_0001", + sensor_id="ir_middle", + attributes={"attr": True}, + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"ir_middle": camera_empty}, + frames={ + 2: Frame( + annotations={ + UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"): Bbox( + sensor_id="ir_middle", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + pos=Point2d(0, 0), + size=Size2d(0, 0), + attributes={"attr": True}, + ) + } + ), + }, + ) + + +def test_add_bbox__just_defaults(camera_empty): + actual = SceneBuilder.empty().add_bbox() + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"rgb_middle": camera_empty}, + frames={ + 1: Frame( + annotations={ + UUID("6c95543d-0000-4000-0000-000000000000"): Bbox( + sensor_id="rgb_middle", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + pos=Point2d(0, 0), + size=Size2d(0, 0), + attributes={}, + ) + } + ), + }, + ) + + +def test_add_cuboid(): + actual = SceneBuilder.empty().add_cuboid( + uid=UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"), + frame_id=2, + object_name="person_0001", + sensor_id="lidar_left", + attributes={"my_attr": 5}, + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"lidar_left": Lidar()}, + frames={ + 2: Frame( + annotations={ + UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"): Cuboid( + sensor_id="lidar_left", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + pos=Point3d(0, 0, 0), + size=Size3d(0, 0, 0), + quat=Quaternion(0, 0, 0, 0), + attributes={"my_attr": 5}, + ) + } + ), + }, + ) + + +def test_add_cuboid__just_defaults(): + actual = SceneBuilder.empty().add_cuboid() + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"lidar": Lidar()}, + frames={ + 1: Frame( + annotations={ + UUID("6c95543d-0000-4000-0000-000000000000"): Cuboid( + sensor_id="lidar", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + pos=Point3d(0, 0, 0), + size=Size3d(0, 0, 0), + quat=Quaternion(0, 0, 0, 0), + attributes={}, + ) + } + ), + }, + ) + + +def test_add_poly2d(camera_empty): + actual = SceneBuilder.empty().add_poly2d( + uid=UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"), + frame_id=2, + object_name="person_0001", + sensor_id="ir_left", + attributes={"my_attr": 5}, + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"ir_left": camera_empty}, + frames={ + 2: Frame( + annotations={ + UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"): Poly2d( + sensor_id="ir_left", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + points=[], + closed=False, + attributes={"my_attr": 5}, + ) + } + ), + }, + ) + + +def test_add_poly2d__just_defaults(camera_empty): + actual = SceneBuilder.empty().add_poly2d() + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"rgb_middle": camera_empty}, + frames={ + 1: Frame( + annotations={ + UUID("6c95543d-0000-4000-0000-000000000000"): Poly2d( + sensor_id="rgb_middle", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + points=[], + closed=False, + attributes={}, + ) + } + ), + }, + ) + + +def test_add_poly3d(): + actual = SceneBuilder.empty().add_poly3d( + uid=UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"), + frame_id=2, + object_name="person_0001", + sensor_id="lidar_right", + attributes={"my_attr": 5}, + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"lidar_right": Lidar()}, + frames={ + 2: Frame( + annotations={ + UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"): Poly3d( + sensor_id="lidar_right", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + points=[], + closed=False, + attributes={"my_attr": 5}, + ) + } + ), + }, + ) + + +def test_add_poly3d__just_defaults(): + actual = SceneBuilder.empty().add_poly3d() + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"lidar": Lidar()}, + frames={ + 1: Frame( + annotations={ + UUID("6c95543d-0000-4000-0000-000000000000"): Poly3d( + sensor_id="lidar", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + points=[], + closed=False, + attributes={}, + ) + } + ), + }, + ) + + +def test_add_seg3d(): + actual = SceneBuilder.empty().add_seg3d( + uid=UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"), + frame_id=2, + object_name="person_0001", + sensor_id="lidar_right", + attributes={"my_attr": 5}, + ) + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"lidar_right": Lidar()}, + frames={ + 2: Frame( + annotations={ + UUID("6c95543d-4d4f-43df-a52d-36bf868e09d8"): Seg3d( + sensor_id="lidar_right", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + point_ids=[], + attributes={"my_attr": 5}, + ) + } + ), + }, + ) + + +def test_add_seg3d__just_defaults(): + actual = SceneBuilder.empty().add_seg3d() + assert actual.result == Scene( + metadata=Metadata(schema_version="1.0.0"), + objects={ + UUID("5c59aad4-0000-4000-0000-000000000000"): Object(name="person_0001", type="person") + }, + sensors={"lidar": Lidar()}, + frames={ + 1: Frame( + annotations={ + UUID("6c95543d-0000-4000-0000-000000000000"): Seg3d( + sensor_id="lidar", + object_id=UUID("5c59aad4-0000-4000-0000-000000000000"), + point_ids=[], + attributes={}, + ) + } + ), + }, + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/test_raillabel/__init__.py b/tests/test_raillabel/__init__.py deleted file mode 100644 index dd5d085..0000000 --- a/tests/test_raillabel/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 diff --git a/tests/test_raillabel/_util/test_clean_dict.py b/tests/test_raillabel/_util/test_clean_dict.py deleted file mode 100644 index cd13563..0000000 --- a/tests/test_raillabel/_util/test_clean_dict.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent)) - -from raillabel._util._clean_dict import _clean_dict - - -def test_clean_dict(): - input_dict = { - "non_empty_field": "non_empty_value", - "none_field": None, - "field_with_len_0": [] - } - - assert _clean_dict(input_dict) == { - "non_empty_field": "non_empty_value", - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/_util/test_warning.py b/tests/test_raillabel/_util/test_warning.py deleted file mode 100644 index 2fadae1..0000000 --- a/tests/test_raillabel/_util/test_warning.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent)) - -from raillabel._util._warning import _warning, _WarningsLogger - - -def test_issue_warning(): - with _WarningsLogger() as logger: - _warning("lorem ipsum") - - assert logger.warnings == [ - "lorem ipsum" - ] - -def test_handover_exception(): - with pytest.raises(RuntimeError) as error: - with _WarningsLogger() as logger: - raise RuntimeError("weewoo something went wrong") - -def test_clear_warnings(): - with _WarningsLogger() as logger1: - _warning("lorem ipsum") - - with _WarningsLogger() as logger2: - pass - - assert len(logger2.warnings) == 0 - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/filter/test_filter.py b/tests/test_raillabel/filter/test_filter.py deleted file mode 100644 index 2ddf213..0000000 --- a/tests/test_raillabel/filter/test_filter.py +++ /dev/null @@ -1,432 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent)) - -import raillabel - - -def delete_sensor_from_data(data: dict, sensor_id: str) -> dict: - del data["openlabel"]["streams"][sensor_id] - del data["openlabel"]["coordinate_systems"][sensor_id] - del data["openlabel"]["coordinate_systems"]["base"]["children"][ - data["openlabel"]["coordinate_systems"]["base"]["children"].index( - sensor_id - ) - ] - - for frame_id in data["openlabel"]["frames"]: - if sensor_id not in data["openlabel"]["frames"][frame_id]["frame_properties"]["streams"]: - continue - - del data["openlabel"]["frames"][frame_id]["frame_properties"]["streams"][sensor_id] - - return data - -@pytest.fixture -def loader(): - return raillabel.load_.loader_classes.LoaderRailLabel() - - -def test_filter_unexpected_kwarg(json_paths): - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - with pytest.raises(TypeError): - raillabel.filter(scene, unsupported_kwarg=[]) - - -def test_mutual_exclusivity(json_paths): - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - with pytest.raises(ValueError): - raillabel.filter(scene, include_frames=[0], exclude_frames=[1, 2]) - - -def test_filter_frames(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["frames"]["1"] - del data["openlabel"]["objects"]["6fe55546-0dd7-4e40-b6b4-bb7ea3445772"] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for include filter - scene_filtered = raillabel.filter(scene, include_frames=[0]) - assert scene_filtered == scene_filtered_ground_truth - - # Tests for exclude filter - scene_filtered = raillabel.filter(scene, exclude_frames=[1]) - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_start(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["frames"]["0"] - del data["openlabel"]["objects"]["b40ba3ad-0327-46ff-9c28-2506cfd6d934"] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for frame filter - scene_filtered = raillabel.filter(scene, start_frame=1) - assert scene_filtered == scene_filtered_ground_truth - - # Tests for timestamp filter - scene_filtered = raillabel.filter(scene, start_timestamp="1632321743.134150") - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_end(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["frames"]["1"] - del data["openlabel"]["objects"]["6fe55546-0dd7-4e40-b6b4-bb7ea3445772"] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for frame filter - scene_filtered = raillabel.filter(scene, end_frame=0) - assert scene_filtered == scene_filtered_ground_truth - - # Tests for timestamp filter - scene_filtered = raillabel.filter(scene, end_timestamp="1632321743.233250") - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_object_ids(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["objects"]["22dedd49-6dcb-413b-87ef-00ccfb532e98"] - del data["openlabel"]["frames"]["0"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for include filter - scene_filtered = raillabel.filter( - scene, - include_object_ids=[ - "6fe55546-0dd7-4e40-b6b4-bb7ea3445772", - "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - ], - ) - - assert scene_filtered == scene_filtered_ground_truth - - # Tests for exclude filter - scene_filtered = raillabel.filter( - scene, exclude_object_ids=["22dedd49-6dcb-413b-87ef-00ccfb532e98"] - ) - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_object_types(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["objects"]["22dedd49-6dcb-413b-87ef-00ccfb532e98"] - del data["openlabel"]["frames"]["0"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for include filter - scene_filtered = raillabel.filter(scene, include_object_types=["person"]) - assert scene_filtered == scene_filtered_ground_truth - - # Tests for exclude filter - scene_filtered = raillabel.filter(scene, exclude_object_types=["train"]) - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_annotation_ids(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["objects"]["22dedd49-6dcb-413b-87ef-00ccfb532e98"] - del data["openlabel"]["frames"]["0"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for include filter - scene_filtered = raillabel.filter( - scene, - include_annotation_ids=[ - "78f0ad89-2750-4a30-9d66-44c9da73a714", - "68b4e02c-40c8-4de0-89ad-bc00ed05a043", - "bebfbae4-61a2-4758-993c-efa846b050a5", - "3f63201c-fb33-4487-aff6-ae0aa5fa976c", - "dc2be700-8ee4-45c4-9256-920b5d55c917", - "c1087f1d-7271-4dee-83ad-519a4e3b78a8", - "50be7fe3-1f43-47ca-b65a-930e6cfacfeb", - "6ba42cbc-484e-4b8d-a022-b23c2bb6643c", - "5f28fa18-8f2a-4a40-a0b6-c0bbedc00f2e", - "e2503c5d-9fe4-4666-b510-ef644c5a766b", - "450ceb81-9778-4e63-bf89-42f3ed9f6747", - ], - ) - - assert scene_filtered == scene_filtered_ground_truth - - # Tests for exclude filter - scene_filtered = raillabel.filter( - scene, - exclude_annotation_ids=[ - "14f58fb0-add7-4ed9-85b3-74615986d854", - "536ac83a-32c8-4fce-8499-ef32716c64a6", - "e53bd5e3-980a-4fa7-a0f9-5a2e59ba663c", - "550df2c3-0e66-483e-bcc6-f3013b7e581b", - "12b21c52-06ea-4269-9805-e7167e7a74ed", - ], - ) - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_annotation_types(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["objects"]["22dedd49-6dcb-413b-87ef-00ccfb532e98"] - - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["cuboid"] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["vec"] - del data["openlabel"]["frames"]["0"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for include filter - scene_filtered = raillabel.filter(scene, include_annotation_types=["bbox", "poly2d", "Num"]) - - assert scene_filtered == scene_filtered_ground_truth - - # Tests for exclude filter - scene_filtered = raillabel.filter(scene, exclude_annotation_types=["cuboid", "Poly3d", "seg3d"]) - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_sensors(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["objects"]["22dedd49-6dcb-413b-87ef-00ccfb532e98"] - - data = delete_sensor_from_data(data, "lidar") - data = delete_sensor_from_data(data, "radar") - - del data["openlabel"]["frames"]["0"]["frame_properties"]["frame_data"][ - "num" - ][-1] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["cuboid"] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["vec"] - del data["openlabel"]["frames"]["0"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - - del data["openlabel"]["frames"]["1"]["frame_properties"]["frame_data"][ - "num" - ][-1] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for include filter - scene_filtered = raillabel.filter(scene, include_sensors=["rgb_middle", "ir_middle"]) - - assert scene_filtered == scene_filtered_ground_truth - - # Tests for exclude filter - scene_filtered = raillabel.filter(scene, exclude_sensors=["lidar"]) - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_include_attribute_ids(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["frames"]["0"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["bbox"][1] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["poly2d"][1] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["cuboid"][1] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["vec"][1] - - del data["openlabel"]["frames"]["1"]["objects"][ - "6fe55546-0dd7-4e40-b6b4-bb7ea3445772" - ]["object_data"]["bbox"][1] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ]["object_data"]["cuboid"][1] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ]["object_data"]["vec"][1] - - data = delete_sensor_from_data(data, "ir_middle") - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for include filter - scene_filtered = raillabel.filter(scene, include_attributes={"test_text_attr0": None}) - - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_exclude_attribute_ids(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["bbox"][0] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["poly2d"][0] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["cuboid"][0] - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["vec"][0] - - del data["openlabel"]["frames"]["1"]["objects"][ - "6fe55546-0dd7-4e40-b6b4-bb7ea3445772" - ]["object_data"]["bbox"][0] - del data["openlabel"]["frames"]["1"]["objects"][ - "6fe55546-0dd7-4e40-b6b4-bb7ea3445772" - ]["object_data"]["poly2d"][0] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ]["object_data"]["cuboid"][0] - del data["openlabel"]["frames"]["1"]["objects"][ - "22dedd49-6dcb-413b-87ef-00ccfb532e98" - ]["object_data"]["vec"][0] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for exclude filter - scene_filtered = raillabel.filter(scene, exclude_attributes={"test_text_attr0": None}) - assert scene_filtered == scene_filtered_ground_truth - - -def test_filter_exclude_attribute_values(json_paths, json_data, loader): - data = json_data["openlabel_v1_short"] - - # Loads scene - scene = raillabel.load(json_paths["openlabel_v1_short"], validate=False) - - # Deletes the excluded data - del data["openlabel"]["frames"]["0"]["objects"][ - "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - ]["object_data"]["poly2d"][0] - data = delete_sensor_from_data(data, "radar") - - # Loads the ground truth filtered data - scene_filtered_ground_truth = loader.load(data) - - # Tests for exclude filter - scene_filtered = raillabel.filter(scene, exclude_attributes={"test_num_attr0": 2}) - assert scene_filtered == scene_filtered_ground_truth - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/format/__init__.py b/tests/test_raillabel/format/__init__.py deleted file mode 100644 index dd5d085..0000000 --- a/tests/test_raillabel/format/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 diff --git a/tests/test_raillabel/format/raillabel/__init__.py b/tests/test_raillabel/format/raillabel/__init__.py deleted file mode 100644 index dd5d085..0000000 --- a/tests/test_raillabel/format/raillabel/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 diff --git a/tests/test_raillabel/format/raillabel/conftest.py b/tests/test_raillabel/format/raillabel/conftest.py deleted file mode 100644 index bc4a860..0000000 --- a/tests/test_raillabel/format/raillabel/conftest.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from .test_attributes import ( - attributes_multiple_types, - attributes_multiple_types_dict, - attributes_single_type, - attributes_single_type_dict, -) -from .test_bbox import bbox, bbox_dict, bbox_train, bbox_train_dict -from .test_cuboid import cuboid, cuboid_dict -from .test_element_data_pointer import ( - element_data_pointer_full, - element_data_pointer_full_dict, - element_data_pointer_minimal, - element_data_pointer_minimal_dict, -) -from .test_frame import frame, frame_dict -from .test_frame_interval import frame_interval, frame_interval_dict -from .test_intrinsics_pinhole import intrinsics_pinhole, intrinsics_pinhole_dict -from .test_intrinsics_radar import intrinsics_radar, intrinsics_radar_dict -from .test_metadata import ( - metadata_full, - metadata_full_dict, - metadata_minimal, - metadata_minimal_dict, -) -from .test_num import num, num_dict -from .test_object import ( - object_person, - object_person_dict, - object_train, - object_train_dict, - objects, - objects_dict, -) -from .test_object_annotation import all_annotations -from .test_object_data import object_data_person_dict, object_data_train_dict -from .test_point2d import point2d, point2d_another, point2d_another_dict, point2d_dict -from .test_point3d import point3d, point3d_another, point3d_another_dict, point3d_dict -from .test_poly2d import poly2d, poly2d_dict -from .test_poly3d import poly3d, poly3d_dict -from .test_quaternion import quaternion, quaternion_dict -from .test_scene import scene, scene_dict -from .test_seg3d import seg3d, seg3d_dict -from .test_sensor import ( - coordinate_systems_dict, - sensor_camera, - sensor_camera_dict, - sensor_lidar, - sensor_lidar_dict, - sensor_radar, - sensor_radar_dict, - sensors, - streams_dict, -) -from .test_sensor_reference import sensor_reference_camera, sensor_reference_camera_dict -from .test_size2d import size2d, size2d_dict -from .test_size3d import size3d, size3d_dict -from .test_transform import transform, transform_dict diff --git a/tests/test_raillabel/format/raillabel/test_attributes.py b/tests/test_raillabel/format/raillabel/test_attributes.py deleted file mode 100644 index 25a6927..0000000 --- a/tests/test_raillabel/format/raillabel/test_attributes.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import _ObjectAnnotation - -# == Fixtures ========================= - -@pytest.fixture -def attributes_single_type_dict() -> dict: - return { - "text": [ - { - "name": "test_text_attr0", - "val": "test_text_attr0_val" - }, - { - "name": "test_text_attr1", - "val": "test_text_attr1_val" - } - ] - } - -@pytest.fixture -def attributes_single_type() -> dict: - return { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - } - - -@pytest.fixture -def attributes_multiple_types_dict() -> dict: - return { - "text": [{ - "name": "text_attr", - "val": "text_val" - }], - "num": [{ - "name": "num_attr", - "val": 0 - }], - "boolean": [{ - "name": "bool_attr", - "val": True - }], - "vec": [{ - "name": "vec_attr", - "val": [0, 1, 2] - }], - } - -@pytest.fixture -def attributes_multiple_types() -> dict: - return { - "text_attr": "text_val", - "num_attr": 0, - "bool_attr": True, - "vec_attr": [0, 1, 2], - } - -# == Tests ============================ - -def test_fromdict__single_type(): - attributes_dict = { - "attributes": { - "text": [ - { - "name": "test_text_attr0", - "val": "test_text_attr0_val" - }, - { - "name": "test_text_attr1", - "val": "test_text_attr1_val" - } - ] - } - } - - assert _ObjectAnnotation._attributes_fromdict(attributes_dict) == { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - } - -def test_fromdict__multiple_types(): - attributes_dict = { - "attributes": { - "text": [{ - "name": "text_attr", - "val": "text_val" - }], - "num": [{ - "name": "num_attr", - "val": 0 - }], - "boolean": [{ - "name": "bool_attr", - "val": True - }], - "vec": [{ - "name": "vec_attr", - "val": [0, 1, 2] - }], - } - } - - assert _ObjectAnnotation._attributes_fromdict(attributes_dict) == { - "text_attr": "text_val", - "num_attr": 0, - "bool_attr": True, - "vec_attr": [0, 1, 2], - } - - -def test_asdict__single_type(): - attributes = { - "test_text_attr0": "test_text_attr0_val", - "test_text_attr1": "test_text_attr1_val", - } - - assert _ObjectAnnotation._attributes_asdict(None, attributes) == { - "text": [ - { - "name": "test_text_attr0", - "val": "test_text_attr0_val" - }, - { - "name": "test_text_attr1", - "val": "test_text_attr1_val" - } - ] - } - -def test_asdict__multiple_types(): - attributes = { - "text_attr": "text_val", - "num_attr": 0, - "bool_attr": True, - "vec_attr": [0, 1, 2], - } - - assert _ObjectAnnotation._attributes_asdict(None, attributes) == { - "text": [{ - "name": "text_attr", - "val": "text_val" - }], - "num": [{ - "name": "num_attr", - "val": 0 - }], - "boolean": [{ - "name": "bool_attr", - "val": True - }], - "vec": [{ - "name": "vec_attr", - "val": [0, 1, 2] - }], - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_bbox.py b/tests/test_raillabel/format/raillabel/test_bbox.py deleted file mode 100644 index 08a8456..0000000 --- a/tests/test_raillabel/format/raillabel/test_bbox.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel._util._warning import _WarningsLogger -from raillabel.format import Bbox - -# == Fixtures ========================= - -@pytest.fixture -def bbox_dict( - sensor_camera, - attributes_multiple_types_dict, - point2d_dict, - size2d_dict, -) -> dict: - return { - "uid": "78f0ad89-2750-4a30-9d66-44c9da73a714", - "name": "rgb_middle__bbox__person", - "val": point2d_dict + size2d_dict, - "coordinate_system": sensor_camera.uid, - "attributes": attributes_multiple_types_dict - } - -@pytest.fixture -def bbox( - point2d, - size2d, - sensor_camera, - attributes_multiple_types, - object_person, -) -> dict: - return Bbox( - uid="78f0ad89-2750-4a30-9d66-44c9da73a714", - pos=point2d, - size=size2d, - sensor=sensor_camera, - attributes=attributes_multiple_types, - object=object_person, - ) - - -@pytest.fixture -def bbox_train_dict(sensor_camera, attributes_single_type_dict, point2d_dict, size2d_dict) -> dict: - return { - "uid": "6a7cfdb7-149d-4987-98dd-79d05a8cc8e6", - "name": "rgb_middle__bbox__train", - "val": point2d_dict + size2d_dict, - "coordinate_system": sensor_camera.uid, - "attributes": attributes_single_type_dict - } - -@pytest.fixture -def bbox_train( - point2d, - size2d, - sensor_camera, - attributes_single_type, - object_train, -) -> dict: - return Bbox( - uid="6a7cfdb7-149d-4987-98dd-79d05a8cc8e6", - pos=point2d, - size=size2d, - sensor=sensor_camera, - attributes=attributes_single_type, - object=object_train, - ) - -# == Tests ============================ - -def test_fromdict( - point2d, point2d_dict, - size2d, size2d_dict, - sensor_camera, sensors, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - bbox = Bbox.fromdict( - { - "uid": "78f0ad89-2750-4a30-9d66-44c9da73a714", - "name": "rgb_middle__bbox__person", - "val": point2d_dict + size2d_dict, - "coordinate_system": sensor_camera.uid, - "attributes": attributes_multiple_types_dict - }, - sensors, - object_person - ) - - assert bbox.uid == "78f0ad89-2750-4a30-9d66-44c9da73a714" - assert bbox.name == "rgb_middle__bbox__person" - assert bbox.pos == point2d - assert bbox.size == size2d - assert bbox.object == object_person - assert bbox.sensor == sensor_camera - assert bbox.attributes == attributes_multiple_types - -def test_fromdict_unknown_coordinate_system_warning( - point2d_dict, - size2d_dict, - sensors, - object_person, -): - with _WarningsLogger() as logger: - bbox = Bbox.fromdict( - { - "uid": "78f0ad89-2750-4a30-9d66-44c9da73a714", - "name": "rgb_middle__bbox__person", - "val": point2d_dict + size2d_dict, - "coordinate_system": "UNKNOWN_COORDINATE_SYSTEM", - }, - sensors, - object_person - ) - - assert len(logger.warnings) == 1 - assert "78f0ad89-2750-4a30-9d66-44c9da73a714" in logger.warnings[0] - assert "'UNKNOWN_COORDINATE_SYSTEM'" in logger.warnings[0] - assert "sensor" in logger.warnings[0] - - assert bbox.sensor is None - - -def test_asdict( - point2d, point2d_dict, - size2d, size2d_dict, - sensor_camera, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - bbox = Bbox( - uid="78f0ad89-2750-4a30-9d66-44c9da73a714", - pos=point2d, - size=size2d, - object=object_person, - sensor=sensor_camera, - attributes=attributes_multiple_types, - ) - - assert bbox.asdict() == { - "uid": "78f0ad89-2750-4a30-9d66-44c9da73a714", - "name": "rgb_middle__bbox__person", - "val": point2d_dict + size2d_dict, - "coordinate_system": sensor_camera.uid, - "attributes": attributes_multiple_types_dict - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_cuboid.py b/tests/test_raillabel/format/raillabel/test_cuboid.py deleted file mode 100644 index 500c356..0000000 --- a/tests/test_raillabel/format/raillabel/test_cuboid.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Cuboid - -# == Fixtures ========================= - -@pytest.fixture -def cuboid_dict( - sensor_lidar, - attributes_multiple_types_dict, - point3d_dict, - size3d_dict, - quaternion_dict -) -> dict: - return { - "uid": "2c6b3de0-86c2-4684-b576-4cfd4f50d6ad", - "name": "lidar__cuboid__person", - "val": point3d_dict + quaternion_dict + size3d_dict, - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict - } - -@pytest.fixture -def cuboid( - point3d, size3d, quaternion, - sensor_lidar, - attributes_multiple_types, - object_person -) -> dict: - return Cuboid( - uid="2c6b3de0-86c2-4684-b576-4cfd4f50d6ad", - pos=point3d, - quat=quaternion, - size=size3d, - object=object_person, - sensor=sensor_lidar, - attributes=attributes_multiple_types, - ) - -# == Tests ============================ - -def test_fromdict( - point3d, point3d_dict, - size3d, size3d_dict, - quaternion, quaternion_dict, - sensor_lidar, sensors, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - cuboid = Cuboid.fromdict( - { - "uid": "2c6b3de0-86c2-4684-b576-4cfd4f50d6ad", - "name": "lidar__cuboid__person", - "val": point3d_dict + quaternion_dict + size3d_dict, - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict - }, - sensors, - object_person - ) - - assert cuboid.uid == "2c6b3de0-86c2-4684-b576-4cfd4f50d6ad" - assert cuboid.name == "lidar__cuboid__person" - assert cuboid.pos == point3d - assert cuboid.quat == quaternion - assert cuboid.size == size3d - assert cuboid.object == object_person - assert cuboid.sensor == sensor_lidar - assert cuboid.attributes == attributes_multiple_types - - -def test_asdict( - point3d, point3d_dict, - size3d, size3d_dict, - quaternion, quaternion_dict, - sensor_lidar, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - cuboid = Cuboid( - uid="2c6b3de0-86c2-4684-b576-4cfd4f50d6ad", - pos=point3d, - quat=quaternion, - size=size3d, - object=object_person, - sensor=sensor_lidar, - attributes=attributes_multiple_types, - ) - - assert cuboid.asdict() == { - "uid": "2c6b3de0-86c2-4684-b576-4cfd4f50d6ad", - "name": "lidar__cuboid__person", - "val": point3d_dict + quaternion_dict + size3d_dict, - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_element_data_pointer.py b/tests/test_raillabel/format/raillabel/test_element_data_pointer.py deleted file mode 100644 index 5ee07a4..0000000 --- a/tests/test_raillabel/format/raillabel/test_element_data_pointer.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel._util._attribute_type import AttributeType -from raillabel.format import ElementDataPointer - -# == Fixtures ========================= - -@pytest.fixture -def element_data_pointer_minimal_dict() -> dict: - return { - "type": "bbox", - "frame_intervals": [], - "attribute_pointers": {}, - } - -@pytest.fixture -def element_data_pointer_minimal(): - return ElementDataPointer( - uid="rgb_middle__bbox__person", - frame_intervals=[], - attribute_pointers={} - ) - - -@pytest.fixture -def element_data_pointer_full_dict(frame_interval_dict) -> dict: - return { - "type": "bbox", - "frame_intervals": [ - frame_interval_dict - ], - "attribute_pointers": { - "text_attr": "text", - "num_attr": "num", - "bool_attr": "boolean", - "vec_attr": "vec", - }, - } - -@pytest.fixture -def element_data_pointer_full(sensor_camera, object_person, frame_interval): - return ElementDataPointer( - uid="rgb_middle__bbox__person", - frame_intervals=[ - frame_interval - ], - attribute_pointers={ - "text_attr": AttributeType.TEXT, - "num_attr": AttributeType.NUM, - "bool_attr": AttributeType.BOOLEAN, - "vec_attr": AttributeType.VEC, - } - ) - -# == Tests ============================ - - -def test_asdict_minimal(sensor_camera, object_person): - element_data_pointer = ElementDataPointer( - uid="rgb_middle__bbox__person", - frame_intervals=[], - attribute_pointers={} - ) - - assert element_data_pointer.asdict() == { - "type": "bbox", - "frame_intervals": [], - "attribute_pointers": {}, - } - -def test_asdict_full(sensor_camera, object_person, frame_interval, frame_interval_dict): - element_data_pointer = ElementDataPointer( - uid="rgb_middle__bbox__person", - frame_intervals=[ - frame_interval - ], - attribute_pointers={ - "text_attr": AttributeType.TEXT, - "num_attr": AttributeType.NUM, - "bool_attr": AttributeType.BOOLEAN, - "vec_attr": AttributeType.VEC, - } - ) - - assert element_data_pointer.asdict() == { - "type": "bbox", - "frame_intervals": [ - frame_interval_dict - ], - "attribute_pointers": { - "text_attr": "text", - "num_attr": "num", - "bool_attr": "boolean", - "vec_attr": "vec", - }, - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_frame.py b/tests/test_raillabel/format/raillabel/test_frame.py deleted file mode 100644 index 06f9c3a..0000000 --- a/tests/test_raillabel/format/raillabel/test_frame.py +++ /dev/null @@ -1,275 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from decimal import Decimal -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel._util._warning import _WarningsLogger -from raillabel.format import Frame - -# == Fixtures ========================= - -@pytest.fixture -def frame_dict( - sensor_reference_camera_dict, - num_dict, - object_person, object_data_person_dict, - object_train, object_data_train_dict, -) -> dict: - return { - "frame_properties": { - "timestamp": "1632321743.100000072", - "streams": { - "rgb_middle": sensor_reference_camera_dict - }, - "frame_data": { - "num": [num_dict] - }, - }, - "objects": { - object_person.uid: object_data_person_dict, - object_train.uid: object_data_train_dict, - } - } - -@pytest.fixture -def frame( - sensor_reference_camera, - num, - all_annotations -) -> dict: - return Frame( - uid=0, - timestamp=Decimal("1632321743.100000072"), - sensors={sensor_reference_camera.sensor.uid: sensor_reference_camera}, - frame_data={num.name: num}, - annotations=all_annotations - ) - -# == Tests ============================ - -def test_fromdict_sensors( - sensor_reference_camera_dict, - sensor_reference_camera, - sensor_camera -): - frame = Frame.fromdict( - uid=0, - data_dict={ - "frame_properties": { - "timestamp": "1632321743.100000072", - "streams": { - "rgb_middle": sensor_reference_camera_dict - } - } - }, - sensors={sensor_camera.uid: sensor_camera}, - objects={}, - ) - - assert frame.uid == 0 - assert frame.timestamp == Decimal("1632321743.100000072") - assert frame.sensors == {sensor_reference_camera.sensor.uid: sensor_reference_camera} - -def test_fromdict_frame_data( - num, num_dict, - sensor_camera -): - frame = Frame.fromdict( - uid=1, - data_dict={ - "frame_properties": { - "frame_data": { - "num": [num_dict] - } - } - }, - sensors={sensor_camera.uid: sensor_camera}, - objects={}, - ) - - assert frame.frame_data == {num.name: num} - -def test_fromdict_annotations( - object_data_person_dict, object_person, - object_data_train_dict, object_train, - sensors, - all_annotations, -): - frame = Frame.fromdict( - uid=2, - data_dict={ - "objects": { - object_person.uid: object_data_person_dict, - object_train.uid: object_data_train_dict, - } - }, - sensors=sensors, - objects={ - object_person.uid: object_person, - object_train.uid: object_train, - }, - ) - - assert frame.annotations == all_annotations - -def test_fromdict_uri_attribute( - bbox_dict, - sensor_reference_camera_dict, sensors, - object_person, objects, -): - bbox_with_uri_attribute = bbox_dict - bbox_with_uri_attribute["attributes"]["text"].append({ - "name": "uri", - "val": "test_uri.png" - }) - - with _WarningsLogger() as logger: - frame = Frame.fromdict( - uid=0, - data_dict={ - "frame_properties": { - "streams": { - "rgb_middle": sensor_reference_camera_dict - } - }, - "objects": { - object_person.uid: { - "object_data": { - "bbox": [bbox_with_uri_attribute] - } - } - } - }, - sensors=sensors, - objects=objects, - ) - - assert len(logger.warnings) == 1 - assert "uri" in logger.warnings[0] - assert bbox_with_uri_attribute["uid"] in logger.warnings[0] - assert "raillabel.save()" in logger.warnings[0] - - assert frame.sensors["rgb_middle"].uri == "test_uri.png" - assert "uri" not in frame.annotations[bbox_with_uri_attribute["uid"]].attributes - -def test_fromdict_duplicate_annotation_uid_warning( - sensors, - object_person, objects, - bbox_dict, cuboid_dict -): - cuboid_dict["uid"] = bbox_dict["uid"] - - with _WarningsLogger() as logger: - frame = Frame.fromdict( - uid=2, - data_dict={ - "objects": { - object_person.uid: { - "object_data": { - "bbox": [bbox_dict], - "cuboid": [cuboid_dict], - } - } - } - }, - sensors=sensors, - objects=objects, - ) - - assert len(logger.warnings) == 1 - assert bbox_dict["uid"] in logger.warnings[0] - assert list(frame.annotations.values())[0].uid != list(frame.annotations.values())[1].uid - - -def test_asdict_sensors( - sensor_reference_camera_dict, - sensor_reference_camera, -): - frame = Frame( - uid=0, - timestamp=Decimal("1632321743.100000072"), - sensors={sensor_reference_camera.sensor.uid: sensor_reference_camera}, - ) - - assert frame.asdict() == { - "frame_properties": { - "timestamp": "1632321743.100000072", - "streams": { - "rgb_middle": sensor_reference_camera_dict - } - } - } - -def test_asdict_frame_data(num, num_dict): - frame = Frame( - uid=0, - frame_data={num.name: num} - ) - - assert frame.asdict() == { - "frame_properties": { - "frame_data": { - "num": [num_dict] - } - } - } - -def test_asdict_object_data( - object_data_person_dict, object_person, - object_data_train_dict, object_train, - all_annotations -): - frame = Frame( - uid=0, - annotations=all_annotations - ) - - assert frame.asdict() == { - "objects": { - object_person.uid: object_data_person_dict, - object_train.uid: object_data_train_dict, - } - } - - -def test_object_data( - object_person, object_train, - bbox, cuboid, poly2d, poly3d, seg3d, - bbox_train -): - frame = Frame( - uid=2, - annotations={ - bbox.uid: bbox, - poly2d.uid: poly2d, - cuboid.uid: cuboid, - poly3d.uid: poly3d, - seg3d.uid: seg3d, - bbox_train.uid: bbox_train, - } - ) - - assert frame.object_data == { - object_person.uid: { - bbox.uid: bbox, - poly2d.uid: poly2d, - cuboid.uid: cuboid, - poly3d.uid: poly3d, - seg3d.uid: seg3d, - }, - object_train.uid: { - bbox_train.uid: bbox_train - } - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-vv"]) diff --git a/tests/test_raillabel/format/raillabel/test_frame_interval.py b/tests/test_raillabel/format/raillabel/test_frame_interval.py deleted file mode 100644 index dbae0de..0000000 --- a/tests/test_raillabel/format/raillabel/test_frame_interval.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import FrameInterval - -# == Fixtures ========================= - -@pytest.fixture -def frame_interval_dict() -> dict: - return { - "frame_start": 12, - "frame_end": 16 - } - -@pytest.fixture -def frame_interval() -> dict: - return FrameInterval( - frame_start=12, - frame_end=16, - ) - -# == Tests ============================ - -def test_fromdict(): - frame_interval = FrameInterval.fromdict( - { - "frame_start": 12, - "frame_end": 16, - } - ) - - assert frame_interval.frame_start == 12 - assert frame_interval.frame_end == 16 - - -def test_asdict(): - frame_interval = FrameInterval( - frame_start=12, - frame_end=16, - ) - - assert frame_interval.asdict() == { - "frame_start": 12, - "frame_end": 16, - } - - -def test_len(): - frame_interval = FrameInterval( - frame_start=12, - frame_end=16, - ) - - assert len(frame_interval) == 5 - - -def test_from_frame_uids_empty(): - frame_uids = [] - - assert FrameInterval.from_frame_uids(frame_uids) == [] - -def test_from_frame_uids_one_frame(): - frame_uids = [1] - - assert FrameInterval.from_frame_uids(frame_uids) == [ - FrameInterval(1, 1) - ] - -def test_from_frame_uids_one_interval(): - frame_uids = [1, 2, 3, 4] - - assert FrameInterval.from_frame_uids(frame_uids) == [ - FrameInterval(1, 4) - ] - -def test_from_frame_uids_multiple_intervals(): - frame_uids = [0, 1, 2, 3, 6, 7, 9, 12, 13, 14] - - assert FrameInterval.from_frame_uids(frame_uids) == [ - FrameInterval(0, 3), - FrameInterval(6, 7), - FrameInterval(9, 9), - FrameInterval(12, 14), - ] - -def test_from_frame_uids_unsorted(): - frame_uids = [5, 2, 1, 3] - - assert FrameInterval.from_frame_uids(frame_uids) == [ - FrameInterval(1, 3), - FrameInterval(5, 5), - ] - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_intrinsics_pinhole.py b/tests/test_raillabel/format/raillabel/test_intrinsics_pinhole.py deleted file mode 100644 index b0b9aae..0000000 --- a/tests/test_raillabel/format/raillabel/test_intrinsics_pinhole.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import IntrinsicsPinhole - -# == Fixtures ========================= - -@pytest.fixture -def intrinsics_pinhole_dict() -> dict: - return { - "camera_matrix": [ - 0.48, 0 , 0.81, 0, - 0 , 0.16, 0.83, 0, - 0 , 0 , 1 , 0 - ], - "distortion_coeffs": [ - 0.49, - 0.69, - 0.31, - 0.81, - 0.99 - ], - "width_px": 2464, - "height_px": 1600 - } - -@pytest.fixture -def intrinsics_pinhole() -> dict: - return IntrinsicsPinhole( - camera_matrix=( - 0.48, 0 , 0.81, 0, - 0 , 0.16, 0.83, 0, - 0 , 0 , 1 , 0 - ), - distortion=( - 0.49, - 0.69, - 0.31, - 0.81, - 0.99 - ), - width_px=2464, - height_px=1600 - ) - -# == Tests ============================ - -def test_fromdict(): - intrinsics_pinhole = IntrinsicsPinhole.fromdict( - { - "camera_matrix": [ - 0.48, 0 , 0.81, 0, - 0 , 0.16, 0.83, 0, - 0 , 0 , 1 , 0 - ], - "distortion_coeffs": [ - 0.49, - 0.69, - 0.31, - 0.81, - 0.99 - ], - "width_px": 2464, - "height_px": 1600 - } - ) - - assert intrinsics_pinhole.camera_matrix == ( - 0.48, 0 , 0.81, 0, - 0 , 0.16, 0.83, 0, - 0 , 0 , 1 , 0 - ) - assert intrinsics_pinhole.distortion == ( - 0.49, - 0.69, - 0.31, - 0.81, - 0.99 - ) - assert intrinsics_pinhole.width_px == 2464 - assert intrinsics_pinhole.height_px == 1600 - - -def test_asdict(): - intrinsics_pinhole = IntrinsicsPinhole( - camera_matrix=( - 0.48, 0 , 0.81, 0, - 0 , 0.16, 0.83, 0, - 0 , 0 , 1 , 0 - ), - distortion=( - 0.49, - 0.69, - 0.31, - 0.81, - 0.99 - ), - width_px=2464, - height_px=1600 - ) - - assert intrinsics_pinhole.asdict() == { - "camera_matrix": [ - 0.48, 0 , 0.81, 0, - 0 , 0.16, 0.83, 0, - 0 , 0 , 1 , 0 - ], - "distortion_coeffs": [ - 0.49, - 0.69, - 0.31, - 0.81, - 0.99 - ], - "width_px": 2464, - "height_px": 1600 - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_intrinsics_radar.py b/tests/test_raillabel/format/raillabel/test_intrinsics_radar.py deleted file mode 100644 index 4dd9f09..0000000 --- a/tests/test_raillabel/format/raillabel/test_intrinsics_radar.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import IntrinsicsRadar - -# == Fixtures ========================= - -@pytest.fixture -def intrinsics_radar_dict() -> dict: - return { - "resolution_px_per_m": 2.856, - "width_px": 2856, - "height_px": 1428, - } - -@pytest.fixture -def intrinsics_radar() -> dict: - return IntrinsicsRadar( - resolution_px_per_m=2.856, - width_px=2856, - height_px=1428, - ) - -# == Tests ============================ - -def test_fromdict(): - intrinsics_radar = IntrinsicsRadar.fromdict( - { - "resolution_px_per_m": 2.856, - "width_px": 2856, - "height_px": 1428, - } - ) - - assert intrinsics_radar.resolution_px_per_m == 2.856 - assert intrinsics_radar.width_px == 2856 - assert intrinsics_radar.height_px == 1428 - - -def test_asdict(): - intrinsics_radar = IntrinsicsRadar( - resolution_px_per_m=2.856, - width_px=2856, - height_px=1428, - ) - - assert intrinsics_radar.asdict() == { - "resolution_px_per_m": 2.856, - "width_px": 2856, - "height_px": 1428, - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_metadata.py b/tests/test_raillabel/format/raillabel/test_metadata.py deleted file mode 100644 index 4aa4d27..0000000 --- a/tests/test_raillabel/format/raillabel/test_metadata.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Metadata - -# == Fixtures ========================= - -@pytest.fixture -def metadata_minimal_dict() -> dict: - return { - "schema_version": "1.0.0" - } - - -@pytest.fixture -def metadata_full_dict() -> dict: - return { - "schema_version": "1.0.0", - "annotator": "test_annotator", - "subschema_version": "2.1.0", - "comment": "test_comment", - "name": "test_project", - "tagged_file": "test_folder", - } - -@pytest.fixture -def metadata_minimal() -> dict: - return Metadata( - schema_version="1.0.0" - ) - - -@pytest.fixture -def metadata_full() -> dict: - return Metadata( - schema_version="1.0.0", - annotator="test_annotator", - subschema_version="2.1.0", - comment="test_comment", - name="test_project", - tagged_file="test_folder", - ) - -# == Tests ============================ - -def test_fromdict_minimal(): - metadata = Metadata.fromdict( - { - "schema_version": "1.0.0" - }, - ) - - assert metadata.schema_version == "1.0.0" - assert metadata.annotator is None - - -def test_fromdict_full(): - metadata = Metadata.fromdict( - { - "schema_version": "1.0.0", - "annotator": "test_annotator", - "subschema_version": "2.1.0", - "comment": "test_comment", - "name": "test_project", - "tagged_file": "test_folder", - }, - "2.1.1" - ) - - assert metadata.annotator == "test_annotator" - assert metadata.schema_version == "1.0.0" - assert metadata.comment == "test_comment" - assert metadata.name == "test_project" - assert metadata.subschema_version == "2.1.1" - assert metadata.tagged_file == "test_folder" - - -def test_fromdict_additional_arg_valid(): - metadata = Metadata.fromdict( - { - "schema_version": "1.0.0", - "additional_argument": "Some Value" - } - ) - - assert metadata.schema_version == "1.0.0" - assert metadata.additional_argument == "Some Value" - - -def test_fromdict_additional_arg_invalid(): - with pytest.raises(KeyError): - Metadata.fromdict( - { - "schema_version": "1.0.0", - "invalid python variable": "Some Value" - } - ) - - -def test_asdict_minimal(): - metadata_dict = Metadata( - schema_version="1.0.0" - ).asdict() - - assert metadata_dict == { - "schema_version": "1.0.0" - } - - -def test_asdict_full(): - metadata_dict = Metadata( - annotator="test_annotator", - schema_version="1.0.0", - comment="test_comment", - name="test_project", - subschema_version="2.1.0", - tagged_file="test_folder", - ).asdict() - - assert metadata_dict == { - "schema_version": "1.0.0", - "annotator": "test_annotator", - "subschema_version": "2.1.0", - "comment": "test_comment", - "name": "test_project", - "tagged_file": "test_folder", - } - - -def test_fromdict_additional_arg(): - metadata = Metadata( - schema_version="1.0.0" - ) - - metadata.additional_argument = "Some Value" - - assert metadata.asdict() == { - "schema_version": "1.0.0", - "additional_argument": "Some Value" - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_num.py b/tests/test_raillabel/format/raillabel/test_num.py deleted file mode 100644 index c14dfb0..0000000 --- a/tests/test_raillabel/format/raillabel/test_num.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Num - -# == Fixtures ========================= - -@pytest.fixture -def num_dict(sensor_camera) -> dict: - return { - "uid": "4e86c449-3B19-410c-aa64-603d46da3b26", - "name": "some_number", - "val": 24, - "coordinate_system": sensor_camera.uid - } - -@pytest.fixture -def num(sensor_camera) -> dict: - return Num( - uid="4e86c449-3B19-410c-aa64-603d46da3b26", - name="some_number", - val=24, - sensor=sensor_camera, - ) - -# == Tests ============================ - -def test_fromdict(sensor_camera): - num = Num.fromdict( - { - "uid": "4e86c449-3B19-410c-aa64-603d46da3b26", - "name": "some_number", - "val": 24, - "coordinate_system": sensor_camera.uid, - }, - { - sensor_camera.uid: sensor_camera - } - ) - - assert num.uid == "4e86c449-3B19-410c-aa64-603d46da3b26" - assert num.name == "some_number" - assert num.val == 24 - assert num.sensor == sensor_camera - - -def test_asdict(sensor_camera): - num = Num( - uid="4e86c449-3B19-410c-aa64-603d46da3b26", - name="some_number", - val=24, - sensor=sensor_camera, - ) - - assert num.asdict() == { - "uid": "4e86c449-3B19-410c-aa64-603d46da3b26", - "name": "some_number", - "val": 24, - "coordinate_system": sensor_camera.uid, - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_object.py b/tests/test_raillabel/format/raillabel/test_object.py deleted file mode 100644 index 647289f..0000000 --- a/tests/test_raillabel/format/raillabel/test_object.py +++ /dev/null @@ -1,427 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import random -import sys -import typing as t -from pathlib import Path -from uuid import UUID, uuid4 - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel._util._attribute_type import AttributeType -from raillabel.format import ( - Bbox, - Cuboid, - Frame, - FrameInterval, - Object, - Point2d, - Point3d, - Quaternion, - Sensor, - Size2d, - Size3d, -) - -# == Fixtures ========================= - -@pytest.fixture -def objects_dict(object_person_dict, object_train_dict) -> dict: - return { - object_person_dict["object_uid"]: object_person_dict["data_dict"], - object_train_dict["object_uid"]: object_train_dict["data_dict"], - } - -@pytest.fixture -def objects(object_person, object_train) -> t.Dict[str, Object]: - return { - object_person.uid: object_person, - object_train.uid: object_train, - } - - -@pytest.fixture -def object_person_dict() -> dict: - return { - "object_uid": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "data_dict": { - "name": "person_0000", - "type": "person", - }, - } - -@pytest.fixture -def object_person() -> dict: - return Object( - uid="b40ba3ad-0327-46ff-9c28-2506cfd6d934", - name="person_0000", - type="person", - ) - - -@pytest.fixture -def object_train_dict() -> dict: - return { - "object_uid": "d51a19be-8bc2-4a82-b66a-03c8de95b0cf", - "data_dict": { - "name": "train_0000", - "type": "train", - }, - } - -@pytest.fixture -def object_train() -> dict: - return Object( - uid="d51a19be-8bc2-4a82-b66a-03c8de95b0cf", - name="train_0000", - type="train", - ) - -# == Tests ============================ - -def test_fromdict(): - object = Object.fromdict( - object_uid="b40ba3ad-0327-46ff-9c28-2506cfd6d934", - data_dict={ - "name": "person_0000", - "type": "person", - } - ) - - assert object.uid == "b40ba3ad-0327-46ff-9c28-2506cfd6d934" - assert object.name == "person_0000" - assert object.type == "person" - - -def test_asdict_no_frames(): - object = Object( - uid="b40ba3ad-0327-46ff-9c28-2506cfd6d934", - name="person_0000", - type="person", - ) - - assert object.asdict() == { - "name": "person_0000", - "type": "person", - } - -def test_asdict_with_frames(): - object = Object( - uid="b40ba3ad-0327-46ff-9c28-2506cfd6d934", - name="person_0000", - type="person", - ) - - frames = { - 0: build_frame(0, - { - object: [build_annotation("rgb_middle__bbox__person", object)] - } - ), - } - - object_dict = object.asdict(frames) - - assert "frame_intervals" in object_dict - assert "object_data_pointers" in object_dict - assert "rgb_middle__bbox__person" in object_dict["object_data_pointers"] - - -def test_frame_intervals(): - object = Object( - uid="b40ba3ad-0327-46ff-9c28-2506cfd6d934", - name="person_0000", - type="person", - ) - - frames = { - 0: build_frame(0, {object: [build_annotation("rgb_middle__bbox__person", object)]}), - 1: build_frame(1, {object: [build_annotation("rgb_middle__bbox__person", object)]}), - 2: build_frame(2, {}), - 3: build_frame(3, {object: [build_annotation("rgb_middle__bbox__person", object)]}), - } - - assert object.frame_intervals(frames) == [ - FrameInterval(0, 1), - FrameInterval(3, 3), - ] - - -def test_object_data_pointers__sensor(): - object = build_object("person") - - frames = { - 0: build_frame(0, - { - object: [ - build_annotation("rgb_middle__bbox__person", object), - build_annotation("lidar__bbox__person", object) - ] - } - ) - } - - object_data_pointers = object.object_data_pointers(frames) - - assert set(object_data_pointers.keys()) == set(["rgb_middle__bbox__person", "lidar__bbox__person"]) - -def test_object_data_pointers__annotation_type(): - object = build_object("person") - - frames = { - 0: build_frame(0, - { - object: [ - build_annotation("rgb_middle__bbox__person", object), - build_annotation("rgb_middle__cuboid__person", object) - ] - } - ) - } - - object_data_pointers = object.object_data_pointers(frames) - - assert set(object_data_pointers.keys()) == set([ - "rgb_middle__bbox__person", - "rgb_middle__cuboid__person" - ]) - -def test_object_data_pointers__one_frame_interval(): - object = build_object("person") - - frames = { - 0: build_frame(0, - { - object: [build_annotation("rgb_middle__bbox__person", object)] - } - ), - 1: build_frame(1, - { - object: [build_annotation("rgb_middle__bbox__person", object)] - } - ), - } - - object_data_pointers = object.object_data_pointers(frames) - - assert len(object_data_pointers) == 1 - assert object_data_pointers["rgb_middle__bbox__person"].frame_intervals == [ - FrameInterval(0, 1) - ] - -def test_object_data_pointers__two_frame_intervals(): - object = build_object("person") - - frames = { - 0: build_frame(0, - { - object: [build_annotation("rgb_middle__bbox__person", object)] - } - ), - 1: build_frame(1, - { - object: [build_annotation("rgb_middle__bbox__person", object)] - } - ), - 8: build_frame(8, - { - object: [build_annotation("rgb_middle__bbox__person", object)] - } - ), - } - - object_data_pointers = object.object_data_pointers(frames) - - assert len(object_data_pointers) == 1 - assert object_data_pointers["rgb_middle__bbox__person"].frame_intervals == [ - FrameInterval(0, 1), - FrameInterval(8, 8), - ] - -def test_object_data_pointers__attributes_one_annotation(): - object = build_object("person") - - frames = { - 0: build_frame(0, - { - object: [ - build_annotation( - name="rgb_middle__bbox__person", - object=object, - attributes={ - "text_attr": "some value", - "num_attr": 0, - "bool_attr": True, - "vec_attr": [0, 1], - } - ), - ] - } - ) - } - - object_data_pointers = object.object_data_pointers(frames) - - assert object_data_pointers["rgb_middle__bbox__person"].attribute_pointers == { - "text_attr": AttributeType.TEXT, - "num_attr": AttributeType.NUM, - "bool_attr": AttributeType.BOOLEAN, - "vec_attr": AttributeType.VEC, - } - -def test_object_data_pointers__attributes_multiple_annotations_with_differing_attributes(): - object = build_object("person") - - frames = { - 0: build_frame(0, - { - object: [ - build_annotation( - name="rgb_middle__bbox__person", - object=object, - attributes={ - "text_attr": "some value", - "num_attr": 0, - } - ), - build_annotation( - name="rgb_middle__bbox__person", - object=object, - attributes={ - "bool_attr": True, - "vec_attr": [0, 1], - } - ), - ] - } - ) - } - - object_data_pointers = object.object_data_pointers(frames) - - assert object_data_pointers["rgb_middle__bbox__person"].attribute_pointers == { - "text_attr": AttributeType.TEXT, - "num_attr": AttributeType.NUM, - "bool_attr": AttributeType.BOOLEAN, - "vec_attr": AttributeType.VEC, - } - -def test_object_data_pointers__multiple_objects_of_differing_type(): - object_person = build_object("person") - object_train = build_object("train") - - frames = { - 0: build_frame(0, - { - object_person: [ - build_annotation("lidar__bbox__person", object_person), - ] - } - ), - 1: build_frame(1, - { - object_train: [ - build_annotation("lidar__bbox__train", object_train), - ] - } - ) - } - - person_object_data_pointers = object_person.object_data_pointers(frames) - assert len(person_object_data_pointers) == 1 - assert person_object_data_pointers["lidar__bbox__person"].frame_intervals == [ - FrameInterval(0, 0) - ] - - train_object_data_pointers = object_train.object_data_pointers(frames) - assert len(train_object_data_pointers) == 1 - assert train_object_data_pointers["lidar__bbox__train"].frame_intervals == [ - FrameInterval(1, 1) - ] - -def test_object_data_pointers__multiple_objects_of_same_type(): - object1 = build_object("person") - object2 = build_object("person") - - frames = { - 0: build_frame(0, - { - object1: [ - build_annotation("lidar__bbox__person", object1), - ] - } - ), - 1: build_frame(1, - { - object2: [ - build_annotation("lidar__bbox__person", object2), - ] - } - ) - } - - object1_data_pointers = object1.object_data_pointers(frames) - assert len(object1_data_pointers) == 1 - assert object1_data_pointers["lidar__bbox__person"].frame_intervals == [ - FrameInterval(0, 0) - ] - - object2_data_pointers = object2.object_data_pointers(frames) - assert len(object2_data_pointers) == 1 - assert object2_data_pointers["lidar__bbox__person"].frame_intervals == [ - FrameInterval(1, 1) - ] - -# == Helpers ========================== - -def build_annotation(name: str, object: Object, attributes: dict={}) -> t.Union[Bbox, Cuboid]: - sensor_uid, ann_type, object_type = tuple(name.split("__")) - - sensor = Sensor(sensor_uid) - - if ann_type == "bbox": - return Bbox( - uid=str(uuid4()), - object=object, - attributes=attributes, - sensor=sensor, - pos=Point2d(50, 100), - size=Size2d(30, 30) - ) - - elif ann_type == "cuboid": - return Cuboid( - uid=str(uuid4()), - object=object, - attributes=attributes, - sensor=sensor, - pos=Point3d(50, 100, 20), - size=Size3d(30, 30, 30), - quat=Quaternion(0, 0, 0, 1), - ) - - else: - raise ValueError() - -def build_frame(uid: int, raw_object_data: t.Dict[Object, t.List[t.Union[Bbox, Cuboid]]]) -> Frame: - annotations = {} - for object, object_data in raw_object_data.items(): - for annotation in object_data: - annotations[annotation.uid] = annotation - - return Frame(uid=uid, annotations=annotations) - -def build_object(type: str) -> Object: - return Object( - uid=str(uuid4()), - name=f"{type}_{str(random.randint(0, 9999)).zfill(4)}", - type=type - ) - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_object_annotation.py b/tests/test_raillabel/format/raillabel/test_object_annotation.py deleted file mode 100644 index 7989e55..0000000 --- a/tests/test_raillabel/format/raillabel/test_object_annotation.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel -from raillabel.format import annotation_classes - -# == Fixtures ========================= - -@pytest.fixture -def all_annotations( - bbox, bbox_train, - cuboid, - poly2d, - poly3d, - seg3d, -): - return { - bbox.uid: bbox, - bbox_train.uid: bbox_train, - cuboid.uid: cuboid, - poly2d.uid: poly2d, - poly3d.uid: poly3d, - seg3d.uid: seg3d, - } - -# == Tests ============================ - -def test_post_init_happy(object_person, point2d, size2d): - raillabel.format.Bbox( - uid="d2764400-8560-4991-a491-ada598b345c8", - object=object_person, - pos=point2d, - size=size2d, - ) - -def test_post_init_unhappy(object_person, point2d): - with pytest.raises(TypeError): - raillabel.format.Bbox( - uid="d2764400-8560-4991-a491-ada598b345c8", - object=object_person, - pos=point2d, - ) - - -def test_annotation_classes(): - assert annotation_classes() == { - "bbox": raillabel.format.Bbox, - "poly2d": raillabel.format.Poly2d, - "cuboid": raillabel.format.Cuboid, - "poly3d": raillabel.format.Poly3d, - "vec": raillabel.format.Seg3d, - } - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_object_data.py b/tests/test_raillabel/format/raillabel/test_object_data.py deleted file mode 100644 index 01093de..0000000 --- a/tests/test_raillabel/format/raillabel/test_object_data.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -# == Fixtures ========================= - -@pytest.fixture -def object_data_person_dict(bbox_dict, poly2d_dict, cuboid_dict, poly3d_dict, seg3d_dict) -> dict: - return { - "object_data": { - "bbox": [bbox_dict], - "poly2d": [poly2d_dict], - "cuboid": [cuboid_dict], - "poly3d": [poly3d_dict], - "vec": [seg3d_dict], - } - } - -@pytest.fixture -def object_data_train_dict(bbox_train_dict) -> dict: - return { - "object_data": { - "bbox": [bbox_train_dict], - } - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_point2d.py b/tests/test_raillabel/format/raillabel/test_point2d.py deleted file mode 100644 index 07b9f4c..0000000 --- a/tests/test_raillabel/format/raillabel/test_point2d.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Point2d - -# == Fixtures ========================= - -@pytest.fixture -def point2d_dict() -> dict: - return [1.5, 222] - -@pytest.fixture -def point2d() -> dict: - return Point2d(1.5, 222) - - -@pytest.fixture -def point2d_another_dict() -> dict: - return [19, 84] - -@pytest.fixture -def point2d_another() -> dict: - return Point2d(19, 84) - -# == Tests ============================ - -def test_fromdict(): - point2d = Point2d.fromdict( - [1.5, 222] - ) - - assert point2d.x == 1.5 - assert point2d.y == 222 - - -def test_asdict(): - point2d = Point2d( - x=1.5, - y=222, - ) - - assert point2d.asdict() == [1.5, 222] - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_point3d.py b/tests/test_raillabel/format/raillabel/test_point3d.py deleted file mode 100644 index 07fdb12..0000000 --- a/tests/test_raillabel/format/raillabel/test_point3d.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Point3d - -# == Fixtures ========================= - -@pytest.fixture -def point3d_dict() -> dict: - return [420, 3.14, 0] - -@pytest.fixture -def point3d() -> dict: - return Point3d(420, 3.14, 0) - - -@pytest.fixture -def point3d_another_dict() -> dict: - return [9, 8, 7] - -@pytest.fixture -def point3d_another() -> dict: - return Point3d(9, 8, 7) - -# == Tests ============================ - -def test_fromdict(): - point3d = Point3d.fromdict( - [420, 3.14, 0] - ) - - assert point3d.x == 420 - assert point3d.y == 3.14 - assert point3d.z == 0 - - -def test_asdict(): - point3d = Point3d( - x=420, - y=3.14, - z=0, - ) - - assert point3d.asdict() == [420, 3.14, 0] - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_poly2d.py b/tests/test_raillabel/format/raillabel/test_poly2d.py deleted file mode 100644 index 95ce7dd..0000000 --- a/tests/test_raillabel/format/raillabel/test_poly2d.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Poly2d - -# == Fixtures ========================= - -@pytest.fixture -def poly2d_dict( - sensor_camera, - attributes_multiple_types_dict, - point2d_dict, point2d_another_dict -) -> dict: - return { - "uid": "d73b5988-767B-47ef-979c-022af60c6ab2", - "name": "rgb_middle__poly2d__person", - "val": point2d_dict + point2d_another_dict, - "coordinate_system": sensor_camera.uid, - "attributes": attributes_multiple_types_dict, - "closed": True, - "mode": "MODE_POLY2D_ABSOLUTE", - } - -@pytest.fixture -def poly2d( - point2d, point2d_another, - sensor_camera, - attributes_multiple_types, - object_person -) -> dict: - return Poly2d( - uid="d73b5988-767B-47ef-979c-022af60c6ab2", - points=[point2d, point2d_another], - object=object_person, - sensor=sensor_camera, - attributes=attributes_multiple_types, - closed=True, - mode="MODE_POLY2D_ABSOLUTE", - ) - -# == Tests ============================ - -def test_fromdict( - point2d, point2d_dict, - point2d_another, point2d_another_dict, - sensor_camera, sensors, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - poly2d = Poly2d.fromdict( - { - "uid": "d73b5988-767B-47ef-979c-022af60c6ab2", - "name": "rgb_middle__poly2d__person", - "val": point2d_dict + point2d_another_dict, - "coordinate_system": sensor_camera.uid, - "attributes": attributes_multiple_types_dict, - "closed": True, - "mode": "MODE_POLY2D_ABSOLUTE", - }, - sensors, - object_person - ) - - assert poly2d.uid == "d73b5988-767B-47ef-979c-022af60c6ab2" - assert poly2d.name == "rgb_middle__poly2d__person" - assert poly2d.points == [point2d, point2d_another] - assert poly2d.object == object_person - assert poly2d.sensor == sensor_camera - assert poly2d.attributes == attributes_multiple_types - assert poly2d.closed == True - assert poly2d.mode == "MODE_POLY2D_ABSOLUTE" - - -def test_asdict( - point2d, point2d_dict, - point2d_another, point2d_another_dict, - sensor_camera, object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - poly2d = Poly2d( - uid="d73b5988-767B-47ef-979c-022af60c6ab2", - points=[point2d, point2d_another], - object=object_person, - sensor=sensor_camera, - attributes=attributes_multiple_types, - closed=True, - mode="MODE_POLY2D_ABSOLUTE", - ) - - assert poly2d.asdict() == { - "uid": "d73b5988-767B-47ef-979c-022af60c6ab2", - "name": "rgb_middle__poly2d__person", - "val": point2d_dict + point2d_another_dict, - "coordinate_system": sensor_camera.uid, - "attributes": attributes_multiple_types_dict, - "closed": True, - "mode": "MODE_POLY2D_ABSOLUTE", - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_poly3d.py b/tests/test_raillabel/format/raillabel/test_poly3d.py deleted file mode 100644 index 7013906..0000000 --- a/tests/test_raillabel/format/raillabel/test_poly3d.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Poly3d - -# == Fixtures ========================= - -@pytest.fixture -def poly3d_dict( - sensor_lidar, - attributes_multiple_types_dict, - point3d_dict, point3d_another_dict -) -> dict: - return { - "uid": "9a9a30f5-D334-4f11-aa3f-c3c83f2935eb", - "name": "lidar__poly3d__person", - "val": point3d_dict + point3d_another_dict, - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict, - "closed": True, - } - -@pytest.fixture -def poly3d( - point3d, point3d_another, - sensor_lidar, - object_person, - attributes_multiple_types -) -> dict: - return Poly3d( - uid="9a9a30f5-D334-4f11-aa3f-c3c83f2935eb", - points=[point3d, point3d_another], - object=object_person, - sensor=sensor_lidar, - attributes=attributes_multiple_types, - closed=True, - ) - -# == Tests ============================ - -def test_fromdict( - point3d, point3d_dict, - point3d_another, point3d_another_dict, - sensor_lidar, sensors, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - poly3d = Poly3d.fromdict( - { - "uid": "9a9a30f5-D334-4f11-aa3f-c3c83f2935eb", - "name": "lidar__poly3d__person", - "val": point3d_dict + point3d_another_dict, - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict, - "closed": True, - }, - sensors, - object_person - ) - - assert poly3d.uid == "9a9a30f5-D334-4f11-aa3f-c3c83f2935eb" - assert poly3d.name == "lidar__poly3d__person" - assert poly3d.points == [point3d, point3d_another] - assert poly3d.object == object_person - assert poly3d.sensor == sensor_lidar - assert poly3d.attributes == attributes_multiple_types - assert poly3d.closed == True - - -def test_asdict( - point3d, point3d_dict, - point3d_another, point3d_another_dict, - sensor_lidar, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - poly3d = Poly3d( - uid="9a9a30f5-D334-4f11-aa3f-c3c83f2935eb", - points=[point3d, point3d_another], - object=object_person, - sensor=sensor_lidar, - attributes=attributes_multiple_types, - closed=True, - ) - - assert poly3d.asdict() == { - "uid": "9a9a30f5-D334-4f11-aa3f-c3c83f2935eb", - "name": "lidar__poly3d__person", - "val": point3d_dict + point3d_another_dict, - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict, - "closed": True, - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_quaternion.py b/tests/test_raillabel/format/raillabel/test_quaternion.py deleted file mode 100644 index fb6de00..0000000 --- a/tests/test_raillabel/format/raillabel/test_quaternion.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Quaternion - -# == Fixtures ========================= - -@pytest.fixture -def quaternion_dict() -> dict: - return [0.75318325, -0.10270147, 0.21430262, -0.61338551] - -@pytest.fixture -def quaternion() -> dict: - return Quaternion(0.75318325, -0.10270147, 0.21430262, -0.61338551) - -# == Tests ============================ - -def test_fromdict(): - quaternion = Quaternion.fromdict( - [ - 0.75318325, - -0.10270147, - 0.21430262, - -0.61338551 - ] - ) - - assert quaternion.x == 0.75318325 - assert quaternion.y == -0.10270147 - assert quaternion.z == 0.21430262 - assert quaternion.w == -0.61338551 - - -def test_asdict(): - quaternion = Quaternion( - x=0.75318325, - y=-0.10270147, - z=0.21430262, - w=-0.61338551 - ) - - assert quaternion.asdict() == [ - 0.75318325, - -0.10270147, - 0.21430262, - -0.61338551 - ] - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_scene.py b/tests/test_raillabel/format/raillabel/test_scene.py deleted file mode 100644 index 3854449..0000000 --- a/tests/test_raillabel/format/raillabel/test_scene.py +++ /dev/null @@ -1,411 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel import exceptions -from raillabel._util._warning import _WarningsLogger -from raillabel.format import Frame, FrameInterval, Scene - -# == Fixtures ========================= - -@pytest.fixture -def scene_dict( - metadata_full_dict, - sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict, - object_person_dict, object_train_dict, - frame, frame_dict, -) -> dict: - return { - "openlabel": { - "metadata": metadata_full_dict, - "streams": { - sensor_camera_dict["uid"]: sensor_camera_dict["stream"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["stream"], - sensor_radar_dict["uid"]: sensor_radar_dict["stream"], - }, - "coordinate_systems": { - "base": { - "type": "local", - "parent": "", - "children": [ - sensor_lidar_dict["uid"], - sensor_camera_dict["uid"], - sensor_radar_dict["uid"], - ] - }, - sensor_camera_dict["uid"]: sensor_camera_dict["coordinate_system"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["coordinate_system"], - sensor_radar_dict["uid"]: sensor_radar_dict["coordinate_system"], - }, - "objects": { - object_person_dict["object_uid"]: object_person_dict["data_dict"], - object_train_dict["object_uid"]: object_train_dict["data_dict"], - }, - "frames": { - frame.uid: frame_dict, - }, - "frame_intervals": [ - { - "frame_start": 0, - "frame_end": 0, - } - ] - } - } - -@pytest.fixture -def scene( - metadata_full, - sensor_camera, sensor_lidar, sensor_radar, - object_person, object_train, - frame -) -> Scene: - return Scene( - metadata=metadata_full, - sensors={ - sensor_lidar.uid: sensor_lidar, - sensor_camera.uid: sensor_camera, - sensor_radar.uid: sensor_radar, - }, - objects={ - object_person.uid: object_person, - object_train.uid: object_train, - }, - frames={frame.uid: frame} - ) - -# == Tests ============================ - -def test_fromdict_metadata( - metadata_full, metadata_full_dict, -): - scene = Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - } - }, - subschema_version=metadata_full.subschema_version, - ) - - scene.metadata.exporter_version = None # necessary for testing on remote - - assert scene.metadata == metadata_full - -def test_fromdict_sensors( - metadata_full_dict, - sensor_camera, sensor_lidar, sensor_radar, - sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict, -): - scene = Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - "streams": { - sensor_camera_dict["uid"]: sensor_camera_dict["stream"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["stream"], - sensor_radar_dict["uid"]: sensor_radar_dict["stream"], - }, - "coordinate_systems": { - "base": { - "type": "local", - "parent": "", - "children": [ - sensor_lidar_dict["uid"], - sensor_camera_dict["uid"], - sensor_radar_dict["uid"], - ] - }, - sensor_camera_dict["uid"]: sensor_camera_dict["coordinate_system"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["coordinate_system"], - sensor_radar_dict["uid"]: sensor_radar_dict["coordinate_system"], - }, - } - } - ) - - assert scene.sensors == { - sensor_lidar.uid: sensor_lidar, - sensor_camera.uid: sensor_camera, - sensor_radar.uid: sensor_radar, - } - -def test_fromdict_missing_coordinate_system( - metadata_full_dict, - sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict, -): - with pytest.raises(exceptions.MissingCoordinateSystemError): - Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - "streams": { - sensor_camera_dict["uid"]: sensor_camera_dict["stream"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["stream"], - }, - "coordinate_systems": { - "base": { - "type": "local", - "parent": "", - "children": [ - sensor_camera_dict["uid"], - ] - }, - sensor_camera_dict["uid"]: sensor_camera_dict["coordinate_system"], - }, - } - } - ) - -def test_fromdict_missing_stream( - metadata_full_dict, - sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict, -): - with pytest.raises(exceptions.MissingStreamError): - Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - "streams": { - sensor_camera_dict["uid"]: sensor_camera_dict["stream"], - }, - "coordinate_systems": { - "base": { - "type": "local", - "parent": "", - "children": [ - sensor_lidar_dict["uid"], - sensor_camera_dict["uid"], - ] - }, - sensor_camera_dict["uid"]: sensor_camera_dict["coordinate_system"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["coordinate_system"], - }, - } - } - ) - -def test_fromdict_unsupported_parent( - metadata_full_dict, - sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict, -): - sensor_camera_dict["coordinate_system"]["parent"] = "unsupported_parent" - - with pytest.raises(exceptions.UnsupportedParentError): - Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - "streams": { - sensor_camera_dict["uid"]: sensor_camera_dict["stream"], - }, - "coordinate_systems": { - "base": { - "type": "local", - "parent": "", - "children": [ - sensor_camera_dict["uid"], - ] - }, - sensor_camera_dict["uid"]: sensor_camera_dict["coordinate_system"], - }, - } - } - ) - -def test_fromdict_objects( - metadata_full, metadata_full_dict, - object_person, object_train, - object_person_dict, object_train_dict, -): - scene = Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - "objects": { - object_person_dict["object_uid"]: object_person_dict["data_dict"], - object_train_dict["object_uid"]: object_train_dict["data_dict"], - }, - } - }, - subschema_version=metadata_full.subschema_version, - ) - - assert scene.objects == { - object_person.uid: object_person, - object_train.uid: object_train, - } - -def test_fromdict_frames( - metadata_full, metadata_full_dict, - streams_dict, coordinate_systems_dict, - objects_dict, - frame, frame_dict, -): - scene = Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - "streams": streams_dict, - "coordinate_systems": coordinate_systems_dict, - "objects": objects_dict, - "frames": { - str(frame.uid): frame_dict, - }, - "frame_intervals": [ - { - "frame_start": 0, - "frame_end": 0, - } - ] - } - }, - subschema_version=metadata_full.subschema_version, - ) - - assert scene.frames == { - frame.uid: frame, - } - -def test_fromdict_duplicate_frame_id(metadata_full_dict): - with _WarningsLogger() as logger: - scene = Scene.fromdict( - { - "openlabel": { - "metadata": metadata_full_dict, - "frames": { - "0": {}, - "00": {}, - } - } - } - ) - - assert len(logger.warnings) == 1 - assert "0" in logger.warnings[0] - assert len(scene.frames) == 1 - - -def test_asdict_sensors( - metadata_full, metadata_full_dict, - sensor_camera, sensor_lidar, sensor_radar, - sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict, -): - scene = Scene( - metadata=metadata_full, - sensors={ - sensor_lidar.uid: sensor_lidar, - sensor_camera.uid: sensor_camera, - sensor_radar.uid: sensor_radar, - } - ) - - assert scene.asdict() == { - "openlabel": { - "metadata": metadata_full_dict, - "streams": { - sensor_camera_dict["uid"]: sensor_camera_dict["stream"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["stream"], - sensor_radar_dict["uid"]: sensor_radar_dict["stream"], - }, - "coordinate_systems": { - "base": { - "type": "local", - "parent": "", - "children": [ - sensor_lidar_dict["uid"], - sensor_camera_dict["uid"], - sensor_radar_dict["uid"], - ] - }, - sensor_camera_dict["uid"]: sensor_camera_dict["coordinate_system"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["coordinate_system"], - sensor_radar_dict["uid"]: sensor_radar_dict["coordinate_system"], - }, - } - } - -def test_asdict_objects( - metadata_full, metadata_full_dict, - object_person, object_train, - object_person_dict, object_train_dict, -): - scene = Scene( - metadata=metadata_full, - objects={ - object_person.uid: object_person, - object_train.uid: object_train, - }, - ) - - assert scene.asdict(calculate_pointers=False) == { - "openlabel": { - "metadata": metadata_full_dict, - "objects": { - object_person_dict["object_uid"]: object_person_dict["data_dict"], - object_train_dict["object_uid"]: object_train_dict["data_dict"], - }, - } - } - -def test_asdict_frames( - metadata_full, metadata_full_dict, - sensors, streams_dict, coordinate_systems_dict, - objects, objects_dict, - frame, frame_dict, -): - scene = Scene( - metadata=metadata_full, - sensors=sensors, - objects=objects, - frames={ - frame.uid: frame, - } - ) - - assert scene.asdict(calculate_pointers=False) == { - "openlabel": { - "metadata": metadata_full_dict, - "streams": streams_dict, - "coordinate_systems": coordinate_systems_dict, - "objects": objects_dict, - "frames": { - str(frame.uid): frame_dict, - }, - "frame_intervals": [ - { - "frame_start": 0, - "frame_end": 0, - } - ] - } - } - - -def test_frame_intervals(metadata_minimal): - scene = Scene( - metadata=metadata_minimal, - frames={ - 1: Frame(1), - 2: Frame(2), - 3: Frame(3), - 8: Frame(8), - } - ) - - assert scene.frame_intervals == [ - FrameInterval(1, 3), - FrameInterval(8, 8), - ] - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_seg3d.py b/tests/test_raillabel/format/raillabel/test_seg3d.py deleted file mode 100644 index 1a82408..0000000 --- a/tests/test_raillabel/format/raillabel/test_seg3d.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Seg3d - -# == Fixtures ========================= - -@pytest.fixture -def seg3d_dict(sensor_lidar, attributes_multiple_types_dict) -> dict: - return { - "uid": "db4e4a77-B926-4a6c-a2a6-e0ecf9d8734a", - "name": "lidar__vec__person", - "val": [586, 789, 173], - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict - } - -@pytest.fixture -def seg3d(sensor_lidar, attributes_multiple_types, object_person) -> dict: - return Seg3d( - uid="db4e4a77-B926-4a6c-a2a6-e0ecf9d8734a", - point_ids=[586, 789, 173], - object=object_person, - sensor=sensor_lidar, - attributes=attributes_multiple_types, - ) - -# == Tests ============================ - -def test_fromdict( - sensor_lidar, sensors, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - seg3d = Seg3d.fromdict( - { - "uid": "db4e4a77-B926-4a6c-a2a6-e0ecf9d8734a", - "name": "lidar__vec__person", - "val": [586, 789, 173], - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict - }, - sensors, - object_person - ) - - assert seg3d.uid == "db4e4a77-B926-4a6c-a2a6-e0ecf9d8734a" - assert seg3d.name == "lidar__vec__person" - assert seg3d.point_ids == [586, 789, 173] - assert seg3d.object == object_person - assert seg3d.sensor == sensor_lidar - assert seg3d.attributes == attributes_multiple_types - - -def test_asdict( - sensor_lidar, sensors, - object_person, - attributes_multiple_types, attributes_multiple_types_dict, -): - seg3d = Seg3d( - uid="db4e4a77-B926-4a6c-a2a6-e0ecf9d8734a", - point_ids=[586, 789, 173], - object=object_person, - sensor=sensor_lidar, - attributes=attributes_multiple_types, - ) - - assert seg3d.asdict() == { - "uid": "db4e4a77-B926-4a6c-a2a6-e0ecf9d8734a", - "name": "lidar__vec__person", - "val": [586, 789, 173], - "coordinate_system": sensor_lidar.uid, - "attributes": attributes_multiple_types_dict - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_sensor.py b/tests/test_raillabel/format/raillabel/test_sensor.py deleted file mode 100644 index 30066f7..0000000 --- a/tests/test_raillabel/format/raillabel/test_sensor.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -import typing as t -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Sensor, SensorType - -# == Fixtures ========================= - -@pytest.fixture -def sensors(sensor_lidar, sensor_camera, sensor_radar) -> t.Dict[str, Sensor]: - return { - sensor_lidar.uid: sensor_lidar, - sensor_camera.uid: sensor_camera, - sensor_radar.uid: sensor_radar, - } - -@pytest.fixture -def streams_dict(sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict) -> dict: - return { - sensor_camera_dict["uid"]: sensor_camera_dict["stream"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["stream"], - sensor_radar_dict["uid"]: sensor_radar_dict["stream"], - } - -@pytest.fixture -def coordinate_systems_dict(sensor_camera_dict, sensor_lidar_dict, sensor_radar_dict) -> dict: - return { - "base": { - "type": "local", - "parent": "", - "children": [ - sensor_lidar_dict["uid"], - sensor_camera_dict["uid"], - sensor_radar_dict["uid"], - ] - }, - sensor_camera_dict["uid"]: sensor_camera_dict["coordinate_system"], - sensor_lidar_dict["uid"]: sensor_lidar_dict["coordinate_system"], - sensor_radar_dict["uid"]: sensor_radar_dict["coordinate_system"], - } - - -@pytest.fixture -def sensor_lidar_dict(transform_dict) -> dict: - return { - "uid": "lidar", - "stream": { - "type": "lidar", - "uri": "/lidar_merged", - }, - "coordinate_system": { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - } - -@pytest.fixture -def sensor_lidar(transform) -> Sensor: - return Sensor( - uid="lidar", - extrinsics=transform, - intrinsics=None, - type=SensorType.LIDAR, - uri="/lidar_merged", - ) - - -@pytest.fixture -def sensor_camera_dict(transform_dict, intrinsics_pinhole_dict) -> dict: - return { - "uid": "rgb_middle", - "stream": { - "type": "camera", - "uri": "/S1206063/image", - "stream_properties": { - "intrinsics_pinhole": intrinsics_pinhole_dict - } - }, - "coordinate_system": { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - } - -@pytest.fixture -def sensor_camera(transform, intrinsics_pinhole) -> Sensor: - return Sensor( - uid="rgb_middle", - extrinsics=transform, - intrinsics=intrinsics_pinhole, - type=SensorType.CAMERA, - uri="/S1206063/image", - ) - - -@pytest.fixture -def sensor_radar_dict(transform_dict, intrinsics_radar_dict) -> dict: - return { - "uid": "radar", - "stream": { - "type": "radar", - "uri": "/talker1/Nvt/Cartesian", - "stream_properties": { - "intrinsics_radar": intrinsics_radar_dict - } - }, - "coordinate_system": { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - } - -@pytest.fixture -def sensor_radar(transform, intrinsics_radar) -> Sensor: - return Sensor( - uid="radar", - extrinsics=transform, - intrinsics=intrinsics_radar, - type=SensorType.RADAR, - uri="/talker1/Nvt/Cartesian", - ) - -# == Tests ============================ - -def test_lidar_fromdict(transform, transform_dict): - sensor = Sensor.fromdict( - uid="lidar", - stream_data_dict={ - "type": "lidar", - "uri": "/lidar_merged", - }, - cs_data_dict={ - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - ) - - assert sensor.uid == "lidar" - assert sensor.extrinsics == transform - assert sensor.intrinsics == None - assert sensor.type == SensorType.LIDAR - assert sensor.uri == "/lidar_merged" - -def test_lidar_asdict(transform, transform_dict): - sensor = Sensor( - uid="lidar", - extrinsics=transform, - intrinsics=None, - type=SensorType.LIDAR, - uri="/lidar_merged", - ) - - assert sensor.asdict() == { - "stream": { - "type": "lidar", - "uri": "/lidar_merged", - }, - "coordinate_system": { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - } - - -def test_camera_fromdict(transform, transform_dict, intrinsics_pinhole, intrinsics_pinhole_dict): - sensor = Sensor.fromdict( - uid="rgb_middle", - stream_data_dict={ - "type": "camera", - "uri": "/S1206063/image", - "stream_properties": { - "intrinsics_pinhole": intrinsics_pinhole_dict - } - }, - cs_data_dict={ - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - ) - - assert sensor.uid == "rgb_middle" - assert sensor.extrinsics == transform - assert sensor.intrinsics == intrinsics_pinhole - assert sensor.type == SensorType.CAMERA - assert sensor.uri == "/S1206063/image" - -def test_camera_asdict(transform, transform_dict, intrinsics_pinhole, intrinsics_pinhole_dict): - sensor = Sensor( - uid="rgb_middle", - extrinsics=transform, - intrinsics=intrinsics_pinhole, - type=SensorType.CAMERA, - uri="/S1206063/image", - ) - - assert sensor.asdict() == { - "stream": { - "type": "camera", - "uri": "/S1206063/image", - "stream_properties": { - "intrinsics_pinhole": intrinsics_pinhole_dict - } - }, - "coordinate_system": { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - } - - -def test_radar_fromdict(transform, transform_dict, intrinsics_radar, intrinsics_radar_dict): - sensor = Sensor.fromdict( - uid="radar", - stream_data_dict={ - "type": "radar", - "uri": "/talker1/Nvt/Cartesian", - "stream_properties": { - "intrinsics_radar": intrinsics_radar_dict - } - }, - cs_data_dict={ - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - ) - - assert sensor.uid == "radar" - assert sensor.extrinsics == transform - assert sensor.intrinsics == intrinsics_radar - assert sensor.type == SensorType.RADAR - assert sensor.uri == "/talker1/Nvt/Cartesian" - -def test_radar_asdict(transform, transform_dict, intrinsics_radar, intrinsics_radar_dict): - sensor = Sensor( - uid="radar", - extrinsics=transform, - intrinsics=intrinsics_radar, - type=SensorType.RADAR, - uri="/talker1/Nvt/Cartesian", - ) - - assert sensor.asdict() == { - "stream": { - "type": "radar", - "uri": "/talker1/Nvt/Cartesian", - "stream_properties": { - "intrinsics_radar": intrinsics_radar_dict - } - }, - "coordinate_system": { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": transform_dict, - } - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_sensor_reference.py b/tests/test_raillabel/format/raillabel/test_sensor_reference.py deleted file mode 100644 index d5a947c..0000000 --- a/tests/test_raillabel/format/raillabel/test_sensor_reference.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from decimal import Decimal -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import SensorReference - -# == Fixtures ========================= - -@pytest.fixture -def sensor_reference_camera_dict() -> dict: - return { - "stream_properties": { - "sync": { - "timestamp": "1632321743.100000072" - } - }, - "uri": "rgb_test0.png" - } - -@pytest.fixture -def sensor_reference_camera(sensor_camera) -> dict: - return SensorReference( - sensor=sensor_camera, - timestamp=Decimal("1632321743.100000072"), - uri="rgb_test0.png" - ) - -# == Tests ============================ - -def test_fromdict(sensor_camera): - sensor_reference = SensorReference.fromdict( - { - "stream_properties": { - "sync": { - "timestamp": "1632321743.100000072" - } - }, - "uri": "rgb_test0.png" - }, - sensor_camera - ) - - assert sensor_reference.sensor == sensor_camera - assert sensor_reference.timestamp == Decimal("1632321743.100000072") - assert sensor_reference.uri == "rgb_test0.png" - - -def test_asdict(sensor_camera): - sensor_reference = SensorReference( - sensor=sensor_camera, - timestamp=Decimal("1632321743.100000072"), - uri="rgb_test0.png" - ) - - assert sensor_reference.asdict() == { - "stream_properties": { - "sync": { - "timestamp": "1632321743.100000072" - } - }, - "uri": "rgb_test0.png" - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_size2d.py b/tests/test_raillabel/format/raillabel/test_size2d.py deleted file mode 100644 index 62d895e..0000000 --- a/tests/test_raillabel/format/raillabel/test_size2d.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Size2d - -# == Fixtures ========================= - -@pytest.fixture -def size2d_dict() -> dict: - return [25, 1.344] - -@pytest.fixture -def size2d() -> dict: - return Size2d(25, 1.344) - -# == Tests ============================ - -def test_fromdict(): - size2d = Size2d.fromdict( - [25, 1.344] - ) - - assert size2d.x == 25 - assert size2d.y == 1.344 - - -def test_asdict(): - size2d = Size2d( - x=25, - y=1.344, - ) - - assert size2d.asdict() == [25, 1.344] - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_size3d.py b/tests/test_raillabel/format/raillabel/test_size3d.py deleted file mode 100644 index bf5290e..0000000 --- a/tests/test_raillabel/format/raillabel/test_size3d.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Size3d - -# == Fixtures ========================= - -@pytest.fixture -def size3d_dict() -> dict: - return [0.35, 0.7, 1.92] - -@pytest.fixture -def size3d() -> dict: - return Size3d(0.35, 0.7, 1.92) - -# == Tests ============================ - -def test_fromdict(): - size3d = Size3d.fromdict( - [0.35, 0.7, 1.92] - ) - - assert size3d.x == 0.35 - assert size3d.y == 0.7 - assert size3d.z == 1.92 - - -def test_asdict(): - size3d = Size3d( - x=0.35, - y=0.7, - z=1.92 - ) - - assert size3d.asdict() == [0.35, 0.7, 1.92] - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/raillabel/test_transform.py b/tests/test_raillabel/format/raillabel/test_transform.py deleted file mode 100644 index 1a57267..0000000 --- a/tests/test_raillabel/format/raillabel/test_transform.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -from raillabel.format import Transform - -# == Fixtures ========================= - -@pytest.fixture -def transform_dict(point3d_dict, quaternion_dict) -> dict: - return { - "translation": point3d_dict, - "quaternion": quaternion_dict - } - -@pytest.fixture -def transform(point3d, quaternion) -> dict: - return Transform( - pos=point3d, - quat=quaternion - ) - -# == Tests ============================ - -def test_fromdict(point3d, point3d_dict, quaternion, quaternion_dict): - transform = Transform.fromdict( - { - "translation": point3d_dict, - "quaternion": quaternion_dict - } - ) - - assert transform.pos == point3d - assert transform.quat == quaternion - - -def test_asdict(point3d, point3d_dict, quaternion, quaternion_dict): - transform = Transform( - pos=point3d, - quat=quaternion - ) - - assert transform.asdict() == { - "translation": point3d_dict, - "quaternion": quaternion_dict - } - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/conftest.py b/tests/test_raillabel/format/understand_ai/conftest.py deleted file mode 100644 index 3ec078f..0000000 --- a/tests/test_raillabel/format/understand_ai/conftest.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -from test_uai_attributes import attributes_raillabel_dict, attributes_uai, attributes_uai_dict -from test_uai_bounding_box_2d import ( - bounding_box_2d_raillabel_dict, - bounding_box_2d_uai, - bounding_box_2d_uai_dict, -) -from test_uai_bounding_box_3d import ( - bounding_box_3d_raillabel_dict, - bounding_box_3d_uai, - bounding_box_3d_uai_dict, -) -from test_uai_coordinate_system import ( - coordinate_system_camera_raillabel_dict, - coordinate_system_camera_translated_uid, - coordinate_system_camera_uai, - coordinate_system_camera_uai_dict, - coordinate_system_lidar_raillabel_dict, - coordinate_system_lidar_translated_uid, - coordinate_system_lidar_uai, - coordinate_system_lidar_uai_dict, - coordinate_system_radar_raillabel_dict, - coordinate_system_radar_translated_uid, - coordinate_system_radar_uai, - coordinate_system_radar_uai_dict, -) -from test_uai_frame import frame_raillabel_dict, frame_uai, frame_uai_dict -from test_uai_metadata import metadata_raillabel_dict, metadata_uai, metadata_uai_dict -from test_uai_point_3d import point_3d_uai, point_3d_uai_dict, point_3d_vec -from test_uai_polygon_2d import polygon_2d_raillabel_dict, polygon_2d_uai, polygon_2d_uai_dict -from test_uai_polyline_2d import polyline_2d_raillabel_dict, polyline_2d_uai, polyline_2d_uai_dict -from test_uai_quaternion import quaternion_uai, quaternion_uai_dict, quaternion_vec -from test_uai_scene import scene_raillabel_dict, scene_uai, scene_uai_dict -from test_uai_segmentation_3d import ( - segmentation_3d_raillabel_dict, - segmentation_3d_uai, - segmentation_3d_uai_dict, -) -from test_uai_sensor_reference import ( - sensor_camera_raillabel_dict, - sensor_camera_uai, - sensor_camera_uai_dict, - sensor_lidar_raillabel_dict, - sensor_lidar_uai, - sensor_lidar_uai_dict, -) -from test_uai_size_3d import size_3d_uai, size_3d_uai_dict, size_3d_vec diff --git a/tests/test_raillabel/format/understand_ai/test_uai_attributes.py b/tests/test_raillabel/format/understand_ai/test_uai_attributes.py deleted file mode 100644 index 23ecf4c..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_attributes.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import pytest - -# == Fixtures ========================= - -@pytest.fixture -def attributes_uai_dict() -> dict: - return { - "isDummy": False, - "carries": "nothing", - "connectedTo": [], - "pose": "upright" - } - -@pytest.fixture -def attributes_uai() -> dict: - return { - "isDummy": False, - "carries": "nothing", - "connectedTo": [], - "pose": "upright" - } - -@pytest.fixture -def attributes_raillabel_dict() -> dict: - return { - "text": [ - { - "name": "carries", - "val": "nothing" - }, - { - "name": "pose", - "val": "upright" - } - ], - "boolean": [ - { - "name": "isDummy", - "val": False - } - ], - "vec": [ - { - "name": "connectedTo", - "val": [] - } - ] - } diff --git a/tests/test_raillabel/format/understand_ai/test_uai_bounding_box_2d.py b/tests/test_raillabel/format/understand_ai/test_uai_bounding_box_2d.py deleted file mode 100644 index 59c89cb..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_bounding_box_2d.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path -from uuid import UUID - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format -from raillabel.format.understand_ai._translation import translate_class_id - -# == Fixtures ========================= - -@pytest.fixture -def bounding_box_2d_uai_dict(sensor_camera_uai_dict, attributes_uai_dict) -> dict: - return { - "id": "2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965", - "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", - "className": "test_class", - "geometry": { - "xMin": 1, - "yMin": 2, - "xMax": 3, - "yMax": 4 - }, - "attributes": attributes_uai_dict, - "sensor": sensor_camera_uai_dict - } - -@pytest.fixture -def bounding_box_2d_uai(attributes_uai, sensor_camera_uai) -> dict: - return uai_format.BoundingBox2d( - id=UUID("2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965"), - object_id=UUID("48c988bd-76f1-423f-b46d-7e7acb859f31"), - class_name="test_class", - x_min=1, - y_min=2, - x_max=3, - y_max=4, - attributes=attributes_uai, - sensor=sensor_camera_uai, - ) - -@pytest.fixture -def bounding_box_2d_raillabel_dict(attributes_raillabel_dict, coordinate_system_camera_translated_uid) -> dict: - return { - "name": "2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965", - "val": [ - 2.0, - 3.0, - 2.0, - 2.0 - ], - "coordinate_system": coordinate_system_camera_translated_uid, - "attributes": attributes_raillabel_dict, - } - - -# == Tests ============================ - -def test_fromdict( - attributes_uai_dict, attributes_uai, - sensor_camera_uai_dict, sensor_camera_uai -): - bounding_box_2d = uai_format.BoundingBox2d.fromdict( - { - "id": "2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965", - "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", - "className": "test_class", - "geometry": { - "xMin": 1, - "yMin": 2, - "xMax": 3, - "yMax": 4 - }, - "attributes": attributes_uai_dict, - "sensor": sensor_camera_uai_dict - } - ) - - assert bounding_box_2d.id == UUID("2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965") - assert bounding_box_2d.object_id == UUID("48c988bd-76f1-423f-b46d-7e7acb859f31") - assert bounding_box_2d.class_name == "test_class" - assert bounding_box_2d.x_min == 1 - assert bounding_box_2d.y_min == 2 - assert bounding_box_2d.x_max == 3 - assert bounding_box_2d.y_max == 4 - assert bounding_box_2d.attributes == attributes_uai - assert bounding_box_2d.sensor == sensor_camera_uai - - -def test_to_raillabel( - attributes_uai, attributes_raillabel_dict, - sensor_camera_uai, sensor_camera_raillabel_dict, coordinate_system_camera_translated_uid, -): - bounding_box_2d = uai_format.BoundingBox2d( - id=UUID("2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965"), - object_id=UUID("48c988bd-76f1-423f-b46d-7e7acb859f31"), - class_name="test_class", - x_min=1, - y_min=2, - x_max=3, - y_max=4, - attributes=attributes_uai, - sensor=sensor_camera_uai, - ) - - data_dict, object_id, translated_class_id, sensor_reference = bounding_box_2d.to_raillabel() - - assert data_dict == { - "name": "2f2a1d7f-56d1-435c-a3ec-d6b8fdaaa965", - "val": [ - 2.0, - 3.0, - 2.0, - 2.0 - ], - "coordinate_system": coordinate_system_camera_translated_uid, - "attributes": attributes_raillabel_dict, - } - assert object_id == str(bounding_box_2d.object_id) - assert translated_class_id == translate_class_id(bounding_box_2d.class_name) - assert sensor_reference == sensor_camera_raillabel_dict - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_bounding_box_3d.py b/tests/test_raillabel/format/understand_ai/test_uai_bounding_box_3d.py deleted file mode 100644 index 3e82bcf..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_bounding_box_3d.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path -from uuid import UUID - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format -from raillabel.format.understand_ai._translation import translate_class_id - -# == Fixtures ========================= - -@pytest.fixture -def bounding_box_3d_uai_dict( - point_3d_uai_dict, size_3d_uai_dict, quaternion_uai_dict, - sensor_lidar_uai_dict, - attributes_uai_dict -) -> dict: - return { - "id": "910399ec-da3e-4d7e-be42-ef8e53e38ca6", - "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", - "className": "test_class", - "geometry": { - "size": size_3d_uai_dict, - "center": point_3d_uai_dict, - "quaternion": quaternion_uai_dict - }, - "attributes": attributes_uai_dict, - "sensor": sensor_lidar_uai_dict, - } - - -@pytest.fixture -def bounding_box_3d_uai( - point_3d_uai, size_3d_uai, quaternion_uai, - attributes_uai, - sensor_lidar_uai -): - return uai_format.BoundingBox3d( - id=UUID("910399ec-da3e-4d7e-be42-ef8e53e38ca6"), - object_id=UUID("48c988bd-76f1-423f-b46d-7e7acb859f31"), - class_name="test_class", - size=size_3d_uai, - center=point_3d_uai, - quaternion=quaternion_uai, - attributes=attributes_uai, - sensor=sensor_lidar_uai, - ) - -@pytest.fixture -def bounding_box_3d_raillabel_dict( - point_3d_vec, size_3d_vec, quaternion_vec, - coordinate_system_lidar_translated_uid, attributes_raillabel_dict -) -> dict: - return { - "name": "910399ec-da3e-4d7e-be42-ef8e53e38ca6", - "val": point_3d_vec + quaternion_vec + size_3d_vec, - "coordinate_system": coordinate_system_lidar_translated_uid, - "attributes": attributes_raillabel_dict - } - - -# == Tests ============================ - -def test_fromdict( - size_3d_uai_dict, point_3d_uai_dict, quaternion_uai_dict, - size_3d_uai, point_3d_uai, quaternion_uai, - sensor_lidar_uai_dict, sensor_lidar_uai, - attributes_uai_dict, attributes_uai, -): - bounding_box_3d = uai_format.BoundingBox3d.fromdict( - { - "id": "910399ec-da3e-4d7e-be42-ef8e53e38ca6", - "objectId": "48c988bd-76f1-423f-b46d-7e7acb859f31", - "className": "test_class", - "geometry": { - "size": size_3d_uai_dict, - "center": point_3d_uai_dict, - "quaternion": quaternion_uai_dict - }, - "attributes": attributes_uai_dict, - "sensor": sensor_lidar_uai_dict, - } - ) - - assert bounding_box_3d.object_id == UUID("48c988bd-76f1-423f-b46d-7e7acb859f31") - assert bounding_box_3d.id == UUID("910399ec-da3e-4d7e-be42-ef8e53e38ca6") - assert bounding_box_3d.class_name == "test_class" - assert bounding_box_3d.size == size_3d_uai - assert bounding_box_3d.center == point_3d_uai - assert bounding_box_3d.quaternion == quaternion_uai - assert bounding_box_3d.attributes == attributes_uai - assert bounding_box_3d.sensor == sensor_lidar_uai - - -def test_to_raillabel( - size_3d_uai, point_3d_uai, quaternion_uai, - point_3d_vec, quaternion_vec, size_3d_vec, - attributes_uai, attributes_raillabel_dict, - sensor_lidar_uai, coordinate_system_lidar_translated_uid, sensor_lidar_raillabel_dict, -): - bounding_box_3d = uai_format.BoundingBox3d( - id=UUID("910399ec-da3e-4d7e-be42-ef8e53e38ca6"), - object_id=UUID("48c988bd-76f1-423f-b46d-7e7acb859f31"), - class_name="test_class", - size=size_3d_uai, - center=point_3d_uai, - quaternion=quaternion_uai, - attributes=attributes_uai, - sensor=sensor_lidar_uai, - ) - - data_dict, object_id, translated_class_id, sensor_reference = bounding_box_3d.to_raillabel() - - assert data_dict == { - "name": "910399ec-da3e-4d7e-be42-ef8e53e38ca6", - "val": point_3d_vec + quaternion_vec + size_3d_vec, - "coordinate_system": coordinate_system_lidar_translated_uid, - "attributes": attributes_raillabel_dict - } - assert object_id == str(bounding_box_3d.object_id) - assert translated_class_id == translate_class_id(bounding_box_3d.class_name) - assert sensor_reference == sensor_lidar_raillabel_dict - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_coordinate_system.py b/tests/test_raillabel/format/understand_ai/test_uai_coordinate_system.py deleted file mode 100644 index 4fe0a80..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_coordinate_system.py +++ /dev/null @@ -1,341 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format - -# == Fixtures ========================= - -@pytest.fixture -def coordinate_system_camera_uai_dict(point_3d_vec, quaternion_vec) -> dict: - return { - "coordinate_system_id": "ir_middle", - "topic": "/A0001781/image", - "frame_id": " A0001781", - "position": point_3d_vec, - "rotation_quaternion": quaternion_vec, - "rotation_matrix": [0] * 9, - "angle_axis_rotation": [0] * 3, - "homogeneous_transform": [0] * 16, - "measured_position": [0, 0, 0], - "camera_matrix": [ - 3535, 0, 319.5, - 0, 3535, 239.5, - 0, 0, 1 , - ], - "dist_coeffs": [0, 1, 2, 3, 4] - } - -@pytest.fixture -def coordinate_system_camera_uai(point_3d_vec, quaternion_vec): - return uai_format.CoordinateSystem( - uid="ir_middle", - topic="/A0001781/image", - frame_id=" A0001781", - position=point_3d_vec, - rotation_quaternion=quaternion_vec, - rotation_matrix=[0] * 9, - angle_axis_rotation=[0] * 3, - homogeneous_transform=[0] * 16, - measured_position=[0, 0, 0], - camera_matrix=[ - 3535, 0, 319.5, - 0, 3535, 239.5, - 0, 0, 1 , - ], - dist_coeffs=[0, 1, 2, 3, 4] - ) - -@pytest.fixture -def coordinate_system_camera_raillabel_dict(point_3d_vec, quaternion_vec) -> dict: - return ( - { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": { - "translation": point_3d_vec, - "quaternion": quaternion_vec - } - }, - { - "type": "camera", - "uri": "/A0001781/image", - "stream_properties": { - "intrinsics_pinhole": { - "camera_matrix": [ - 3535, 0, 319.5, 0, - 0, 3535, 239.5, 0, - 0, 0, 1 , 0, - ], - "distortion_coeffs": [0, 1, 2, 3, 4], - "width_px": 640, - "height_px": 480, - } - } - } - ) - -@pytest.fixture -def coordinate_system_camera_translated_uid() -> dict: - return "ir_middle" - - -@pytest.fixture -def coordinate_system_lidar_uai_dict(point_3d_vec, quaternion_vec) -> dict: - return { - "coordinate_system_id": "LIDAR", - "topic": "/lidar_merged", - "frame_id": "lidar_merged", - "position": point_3d_vec, - "rotation_quaternion": quaternion_vec, - "rotation_matrix": [0] * 9, - "angle_axis_rotation": [0] * 3, - "homogeneous_transform": [0] * 16, - } - -@pytest.fixture -def coordinate_system_lidar_uai(point_3d_vec, quaternion_vec): - return uai_format.CoordinateSystem( - uid="LIDAR", - topic="/lidar_merged", - frame_id="lidar_merged", - position=point_3d_vec, - rotation_quaternion=quaternion_vec, - rotation_matrix=[0] * 9, - angle_axis_rotation=[0] * 3, - homogeneous_transform=[0] * 16, - ) - -@pytest.fixture -def coordinate_system_lidar_raillabel_dict(point_3d_vec, quaternion_vec) -> dict: - return ( - { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": { - "translation": point_3d_vec, - "quaternion": quaternion_vec - } - }, - { - "type": "lidar", - "uri": "/lidar_merged", - } - ) - -@pytest.fixture -def coordinate_system_lidar_translated_uid() -> dict: - return "lidar" - - -@pytest.fixture -def coordinate_system_radar_uai_dict(point_3d_vec, quaternion_vec) -> dict: - return { - "coordinate_system_id": "radar", - "rotation_around_z_in_degrees": 1.22869, - "topic": "/talker1/Nvt/Cartesian", - "frame_id": "navtech", - "position": point_3d_vec, - "rotation_quaternion": quaternion_vec, - "rotation_matrix": [0] * 9, - "angle_axis_rotation": [0] * 3, - "homogeneous_transform": [0] * 16, - } - -@pytest.fixture -def coordinate_system_radar_uai(point_3d_vec, quaternion_vec): - return uai_format.CoordinateSystem( - uid="radar", - topic="/talker1/Nvt/Cartesian", - frame_id="navtech", - position=point_3d_vec, - rotation_quaternion=quaternion_vec, - rotation_matrix=[0] * 9, - angle_axis_rotation=[0] * 3, - homogeneous_transform=[0] * 16, - ) - -@pytest.fixture -def coordinate_system_radar_raillabel_dict(point_3d_vec, quaternion_vec) -> dict: - return ( - { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": { - "translation": point_3d_vec, - "quaternion": quaternion_vec - } - }, - { - "type": "radar", - "uri": "/talker1/Nvt/Cartesian", - "stream_properties": { - "intrinsics_radar": { - "resolution_px_per_m": 2.856, - "width_px": 2856, - "height_px": 1428 - } - } - } - ) - -@pytest.fixture -def coordinate_system_radar_translated_uid() -> dict: - return "radar" - -# == Tests ============================ - -def test_fromdict(point_3d_vec, quaternion_vec): - coordinate_system = uai_format.CoordinateSystem.fromdict( - { - "coordinate_system_id": "ir_middle", - "topic": "/A0001781/image", - "frame_id": " A0001781", - "position": point_3d_vec, - "rotation_quaternion": quaternion_vec, - "rotation_matrix": [0] * 9, - "angle_axis_rotation": [0] * 3, - "homogeneous_transform": [0] * 16, - "measured_position": [0, 0, 0], - "camera_matrix": [ - 3535, 0, 319.5, - 0, 3535, 239.5, - 0, 0, 1 , - ], - "dist_coeffs": [0, 1, 2, 3, 4] - } - ) - - assert coordinate_system.uid == "ir_middle" - assert coordinate_system.topic == "/A0001781/image" - assert coordinate_system.frame_id == " A0001781" - assert coordinate_system.position == point_3d_vec - assert coordinate_system.rotation_quaternion == quaternion_vec - assert coordinate_system.rotation_matrix == [0] * 9 - assert coordinate_system.angle_axis_rotation == [0] * 3 - assert coordinate_system.homogeneous_transform == [0] * 16 - assert coordinate_system.measured_position == [0, 0, 0] - assert coordinate_system.camera_matrix == [ - 3535, 0, 319.5, - 0, 3535, 239.5, - 0, 0, 1 , - ] - assert coordinate_system.dist_coeffs == [0, 1, 2, 3, 4] - - -def test_to_raillabel__coordinate_system(point_3d_vec, quaternion_vec): - coordinate_system = uai_format.CoordinateSystem( - uid="ir_middle", - topic="/A0001781/image", - frame_id=" A0001781", - position=point_3d_vec, - rotation_quaternion=quaternion_vec, - rotation_matrix=[0] * 9, - angle_axis_rotation=[0] * 3, - homogeneous_transform=[0] * 16, - measured_position=[0, 0, 0], - camera_matrix=[ - 3535, 0, 319.5, - 0, 3535, 239.5, - 0, 0, 1 , - ], - dist_coeffs=[0, 1, 2, 3, 4] - ) - - assert coordinate_system.to_raillabel()[0] == { - "type": "sensor", - "parent": "base", - "pose_wrt_parent": { - "translation": point_3d_vec, - "quaternion": quaternion_vec - } - } - -def test_to_raillabel__stream__camera(point_3d_vec, quaternion_vec): - coordinate_system = uai_format.CoordinateSystem( - uid="ir_middle", - topic="/A0001781/image", - frame_id=" A0001781", - position=point_3d_vec, - rotation_quaternion=quaternion_vec, - rotation_matrix=[0] * 9, - angle_axis_rotation=[0] * 3, - homogeneous_transform=[0] * 16, - measured_position=[0, 0, 0], - camera_matrix=[ - 3535, 0, 319.5, - 0, 3535, 239.5, - 0, 0, 1 , - ], - dist_coeffs=[0, 1, 2, 3, 4] - ) - - assert coordinate_system.to_raillabel()[1] == { - "type": "camera", - "uri": "/A0001781/image", - "stream_properties": { - "intrinsics_pinhole": { - "camera_matrix": [ - 3535, 0, 319.5, 0, - 0, 3535, 239.5, 0, - 0, 0, 1 , 0, - ], - "distortion_coeffs": [0, 1, 2, 3, 4], - "width_px": 640, - "height_px": 480, - } - } - } - -def test_to_raillabel__stream__lidar(point_3d_vec, quaternion_vec): - coordinate_system = uai_format.CoordinateSystem( - uid="LIDAR", - topic="/lidar_merged", - frame_id="lidar_merged", - position=point_3d_vec, - rotation_quaternion=quaternion_vec, - rotation_matrix=[0] * 9, - angle_axis_rotation=[0] * 3, - homogeneous_transform=[0] * 16, - ) - - assert coordinate_system.to_raillabel()[1] == { - "type": "lidar", - "uri": "/lidar_merged", - } - -def test_to_raillabel__stream__radar(point_3d_vec, quaternion_vec): - coordinate_system = uai_format.CoordinateSystem( - uid="radar", - topic="/talker1/Nvt/Cartesian", - frame_id="navtech", - position=point_3d_vec, - rotation_quaternion=quaternion_vec, - rotation_matrix=[0] * 9, - angle_axis_rotation=[0] * 3, - homogeneous_transform=[0] * 16, - ) - - assert coordinate_system.to_raillabel()[1] == { - "type": "radar", - "uri": "/talker1/Nvt/Cartesian", - "stream_properties": { - "intrinsics_radar": { - "resolution_px_per_m": 2.856, - "width_px": 2856, - "height_px": 1428 - } - } - } - - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_frame.py b/tests/test_raillabel/format/understand_ai/test_uai_frame.py deleted file mode 100644 index 0e1496a..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_frame.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path -from uuid import UUID - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format -from raillabel._util._warning import _WarningsLogger - -# == Fixtures ========================= - -@pytest.fixture -def frame_uai_dict( - bounding_box_2d_uai_dict, - bounding_box_3d_uai_dict, - polygon_2d_uai_dict, - polyline_2d_uai_dict, - segmentation_3d_uai_dict, - sensor_lidar_uai_dict, -) -> dict: - return { - "frameId": "000", - "timestamp": sensor_lidar_uai_dict["timestamp"], - "annotations": { - "2D_BOUNDING_BOX": [bounding_box_2d_uai_dict], - "2D_POLYLINE": [polyline_2d_uai_dict], - "2D_POLYGON": [polygon_2d_uai_dict], - "3D_BOUNDING_BOX": [bounding_box_3d_uai_dict], - "3D_SEGMENTATION": [segmentation_3d_uai_dict], - } - } - -@pytest.fixture -def frame_uai( - bounding_box_2d_uai, - bounding_box_3d_uai, - polygon_2d_uai, - polyline_2d_uai, - segmentation_3d_uai, - sensor_lidar_uai, -): - return uai_format.Frame( - id=0, - timestamp=sensor_lidar_uai.timestamp, - bounding_box_2ds={str(bounding_box_2d_uai.id): bounding_box_2d_uai}, - bounding_box_3ds={str(bounding_box_3d_uai.id): bounding_box_3d_uai}, - polygon_2ds={str(polygon_2d_uai.id): polygon_2d_uai}, - polyline_2ds={str(polyline_2d_uai.id): polyline_2d_uai}, - segmentation_3ds={str(segmentation_3d_uai.id): segmentation_3d_uai}, - ) - -@pytest.fixture -def frame_raillabel_dict( - bounding_box_2d_uai, bounding_box_2d_raillabel_dict, - bounding_box_3d_raillabel_dict, - polygon_2d_uai, polygon_2d_raillabel_dict, - polyline_2d_uai, polyline_2d_raillabel_dict, - segmentation_3d_uai, segmentation_3d_raillabel_dict, - sensor_lidar_uai, sensor_lidar_raillabel_dict, coordinate_system_lidar_translated_uid, - sensor_camera_raillabel_dict, coordinate_system_camera_translated_uid, -) -> dict: - return { - "frame_properties": { - "timestamp": str(sensor_lidar_uai.timestamp), - "streams": { - coordinate_system_camera_translated_uid: sensor_camera_raillabel_dict, - coordinate_system_lidar_translated_uid: sensor_lidar_raillabel_dict, - } - }, - "objects": { - str(bounding_box_2d_uai.object_id): { - "object_data": { - "bbox": [bounding_box_2d_raillabel_dict], - "cuboid": [bounding_box_3d_raillabel_dict], - } - }, - str(polygon_2d_uai.object_id): { - "object_data": { - "poly2d": [polygon_2d_raillabel_dict] - } - }, - str(polyline_2d_uai.object_id): { - "object_data": { - "poly2d": [polyline_2d_raillabel_dict] - } - }, - str(segmentation_3d_uai.object_id): { - "object_data": { - "vec": [segmentation_3d_raillabel_dict] - } - }, - } - } - -# == Tests ============================ - -def test_fromdict( - bounding_box_2d_uai_dict, bounding_box_2d_uai, - bounding_box_3d_uai_dict, bounding_box_3d_uai, - polygon_2d_uai_dict, polygon_2d_uai, - polyline_2d_uai_dict, polyline_2d_uai, - segmentation_3d_uai_dict, segmentation_3d_uai, - sensor_lidar_uai_dict, sensor_lidar_uai, -): - frame = uai_format.Frame.fromdict( - { - "frameId": "000", - "timestamp": sensor_lidar_uai_dict["timestamp"], - "annotations": { - "2D_BOUNDING_BOX": [bounding_box_2d_uai_dict], - "2D_POLYLINE": [polyline_2d_uai_dict], - "2D_POLYGON": [polygon_2d_uai_dict], - "3D_BOUNDING_BOX": [bounding_box_3d_uai_dict], - "3D_SEGMENTATION": [segmentation_3d_uai_dict], - } - } - ) - - assert frame.id == 0 - assert frame.timestamp == sensor_lidar_uai.timestamp - assert frame.bounding_box_2ds == {str(bounding_box_2d_uai.id): bounding_box_2d_uai} - assert frame.bounding_box_3ds == {str(bounding_box_3d_uai.id): bounding_box_3d_uai} - assert frame.polygon_2ds == {str(polygon_2d_uai.id): polygon_2d_uai} - assert frame.polyline_2ds == {str(polyline_2d_uai.id): polyline_2d_uai} - assert frame.segmentation_3ds == {str(segmentation_3d_uai.id): segmentation_3d_uai} - - -def test_to_raillabel( - bounding_box_2d_uai, bounding_box_2d_raillabel_dict, - bounding_box_3d_uai, bounding_box_3d_raillabel_dict, - polygon_2d_uai, polygon_2d_raillabel_dict, - polyline_2d_uai, polyline_2d_raillabel_dict, - segmentation_3d_uai, segmentation_3d_raillabel_dict, - sensor_lidar_uai, sensor_lidar_raillabel_dict, coordinate_system_lidar_translated_uid, - sensor_camera_raillabel_dict, coordinate_system_camera_translated_uid, -): - frame = uai_format.Frame( - id=0, - timestamp=sensor_lidar_uai.timestamp, - bounding_box_2ds={str(bounding_box_2d_uai.id): bounding_box_2d_uai}, - bounding_box_3ds={str(bounding_box_3d_uai.id): bounding_box_3d_uai}, - polygon_2ds={str(polygon_2d_uai.id): polygon_2d_uai}, - polyline_2ds={str(polyline_2d_uai.id): polyline_2d_uai}, - segmentation_3ds={str(segmentation_3d_uai.id): segmentation_3d_uai}, - ) - - assert frame.to_raillabel() == { - "frame_properties": { - "timestamp": str(sensor_lidar_uai.timestamp), - "streams": { - coordinate_system_camera_translated_uid: sensor_camera_raillabel_dict, - coordinate_system_lidar_translated_uid: sensor_lidar_raillabel_dict, - } - }, - "objects": { - str(bounding_box_2d_uai.object_id): { - "object_data": { - "bbox": [bounding_box_2d_raillabel_dict], - "cuboid": [bounding_box_3d_raillabel_dict], - } - }, - str(polygon_2d_uai.object_id): { - "object_data": { - "poly2d": [polygon_2d_raillabel_dict] - } - }, - str(polyline_2d_uai.object_id): { - "object_data": { - "poly2d": [polyline_2d_raillabel_dict] - } - }, - str(segmentation_3d_uai.object_id): { - "object_data": { - "vec": [segmentation_3d_raillabel_dict] - } - }, - } - } - - -def test_warning_duplicate_annotation_id( - bounding_box_2d_uai_dict, polyline_2d_uai_dict, - sensor_lidar_uai_dict -): - polyline_2d_uai_dict["id"] = bounding_box_2d_uai_dict["id"] - - frame_dict = { - "frameId": "000", - "timestamp": sensor_lidar_uai_dict["timestamp"], - "annotations": { - "2D_BOUNDING_BOX": [bounding_box_2d_uai_dict], - "2D_POLYLINE": [polyline_2d_uai_dict], - "2D_POLYGON": [], - "3D_BOUNDING_BOX": [], - "3D_SEGMENTATION": [], - } - } - - with _WarningsLogger() as logger: - uai_format.Frame.fromdict(frame_dict) - - assert len(logger.warnings) == 1 - assert bounding_box_2d_uai_dict["id"] in logger.warnings[0] - - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-vv"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_metadata.py b/tests/test_raillabel/format/understand_ai/test_uai_metadata.py deleted file mode 100644 index d890874..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_metadata.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format - -# == Fixtures ========================= - -@pytest.fixture -def metadata_uai_dict() -> dict: - return { - "clip_id": "db_3_2021-09-22-14-28-01_2021-09-22-14-44-03", - "external_clip_id": "2021-09-22-14-28-01_2021-09-22-14-44-03", - "project_id": "trains_4", - "export_time": "2023-04-20 01:38 UTC", - "exporter_version": "1.0.0", - "coordinate_system_3d": "FLU", - "coordinate_system_reference": "SENSOR", - "folder_name": "2021-09-22-14-28-01_2021-09-22-14-44-03", - } - -@pytest.fixture -def metadata_uai(): - return uai_format.Metadata( - clip_id="db_3_2021-09-22-14-28-01_2021-09-22-14-44-03", - external_clip_id="2021-09-22-14-28-01_2021-09-22-14-44-03", - project_id="trains_4", - export_time="2023-04-20 01:38 UTC", - exporter_version="1.0.0", - coordinate_system_3d="FLU", - coordinate_system_reference="SENSOR", - folder_name="2021-09-22-14-28-01_2021-09-22-14-44-03", - ) - -@pytest.fixture -def metadata_raillabel_dict(json_data) -> dict: - return { - "annotator": "understandAI GmbH", - "schema_version": "1.0.0", - "name": "2021-09-22-14-28-01_2021-09-22-14-44-03", - "subschema_version": json_data["raillabel_schema"]["version"], - "tagged_file": "2021-09-22-14-28-01_2021-09-22-14-44-03" - } - - -# == Tests ============================ - -def test_fromdict(): - metadata = uai_format.Metadata.fromdict( - { - "clip_id": "db_3_2021-09-22-14-28-01_2021-09-22-14-44-03", - "external_clip_id": "2021-09-22-14-28-01_2021-09-22-14-44-03", - "project_id": "trains_4", - "export_time": "2023-04-20 01:38 UTC", - "exporter_version": "1.0.0", - "coordinate_system_3d": "FLU", - "coordinate_system_reference": "SENSOR", - "folder_name": "2021-09-22-14-28-01_2021-09-22-14-44-03", - } - ) - - assert metadata.clip_id == "db_3_2021-09-22-14-28-01_2021-09-22-14-44-03" - assert metadata.external_clip_id == "2021-09-22-14-28-01_2021-09-22-14-44-03" - assert metadata.project_id == "trains_4" - assert metadata.export_time == "2023-04-20 01:38 UTC" - assert metadata.exporter_version == "1.0.0" - assert metadata.coordinate_system_3d == "FLU" - assert metadata.coordinate_system_reference == "SENSOR" - assert metadata.folder_name == "2021-09-22-14-28-01_2021-09-22-14-44-03" - - -def test_to_raillabel(json_data): - metadata = uai_format.Metadata( - clip_id="db_3_2021-09-22-14-28-01_2021-09-22-14-44-03", - external_clip_id="2021-09-22-14-28-01_2021-09-22-14-44-03", - project_id="trains_4", - export_time="2023-04-20 01:38 UTC", - exporter_version="1.0.0", - coordinate_system_3d="FLU", - coordinate_system_reference="SENSOR", - folder_name="2021-09-22-14-28-01_2021-09-22-14-44-03", - ) - - assert metadata.to_raillabel() == { - "annotator": "understandAI GmbH", - "schema_version": "1.0.0", - "name": "2021-09-22-14-28-01_2021-09-22-14-44-03", - "subschema_version": json_data["raillabel_schema"]["version"], - "tagged_file": "2021-09-22-14-28-01_2021-09-22-14-44-03" - } - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_point_3d.py b/tests/test_raillabel/format/understand_ai/test_uai_point_3d.py deleted file mode 100644 index 341eacb..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_point_3d.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format - -# == Fixtures ========================= - -@pytest.fixture -def point_3d_uai_dict() -> dict: - return { - "x": 0, - "y": 1, - "z": 2, - } - -@pytest.fixture -def point_3d_uai() -> dict: - return uai_format.Point3d( - x=0, - y=1, - z=2, - ) - -@pytest.fixture -def point_3d_vec() -> dict: - return [0, 1, 2] - -# == Tests ============================ - -def test_fromdict(): - point_3d = uai_format.Point3d.fromdict( - { - "x": 0, - "y": 1, - "z": 2, - } - ) - - assert point_3d.x == 0 - assert point_3d.y == 1 - assert point_3d.z == 2 - - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_polygon_2d.py b/tests/test_raillabel/format/understand_ai/test_uai_polygon_2d.py deleted file mode 100644 index 3b7ef99..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_polygon_2d.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path -from uuid import UUID - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format -from raillabel.format.understand_ai._translation import translate_class_id - -# == Fixtures ========================= - -@pytest.fixture -def polygon_2d_uai_dict(sensor_camera_uai_dict, attributes_uai_dict) -> dict: - return { - "id": "0f90cffa-2b6b-4e09-8fc2-527769a94e0a", - "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", - "className": "test_class", - "geometry": { - "points": [ - [127.71153737657284, -0.3861000079676791], - [127.4762636010818, 328.04436391207815], - [115.77703250958459, 334.4789410124016], - [115.01063176442402, 411.0810690770479], - ] - }, - "attributes": attributes_uai_dict, - "sensor": sensor_camera_uai_dict - } - - -@pytest.fixture -def polygon_2d_uai(attributes_uai, sensor_camera_uai) -> dict: - return uai_format.Polygon2d( - id=UUID("0f90cffa-2b6b-4e09-8fc2-527769a94e0a"), - object_id=UUID("58e7edd8-a7ee-4775-a837-e6dd375e8150"), - class_name="test_class", - points=[ - (127.71153737657284, -0.3861000079676791), - (127.4762636010818, 328.04436391207815), - (115.77703250958459, 334.4789410124016), - (115.01063176442402, 411.0810690770479), - ], - attributes=attributes_uai, - sensor=sensor_camera_uai, - ) - -@pytest.fixture -def polygon_2d_raillabel_dict(attributes_raillabel_dict, coordinate_system_camera_translated_uid) -> dict: - return { - "name": "0f90cffa-2b6b-4e09-8fc2-527769a94e0a", - "val": [ - 127.71153737657284, -0.3861000079676791, - 127.4762636010818, 328.04436391207815, - 115.77703250958459, 334.4789410124016, - 115.01063176442402, 411.0810690770479, - ], - "mode": "MODE_POLY2D_ABSOLUTE", - "closed": True, - "coordinate_system": coordinate_system_camera_translated_uid, - "attributes": attributes_raillabel_dict, - } - - -# == Tests ============================ - -def test_fromdict( - attributes_uai_dict, attributes_uai, - sensor_camera_uai_dict, sensor_camera_uai -): - polygon_2d = uai_format.Polygon2d.fromdict( - { - "id": "0f90cffa-2b6b-4e09-8fc2-527769a94e0a", - "objectId": "58e7edd8-a7ee-4775-a837-e6dd375e8150", - "className": "test_class", - "geometry": { - "points": [ - [127.71153737657284, -0.3861000079676791], - [127.4762636010818, 328.04436391207815], - [115.77703250958459, 334.4789410124016], - [115.01063176442402, 411.0810690770479], - ] - }, - "attributes": attributes_uai_dict, - "sensor": sensor_camera_uai_dict - } - ) - - assert polygon_2d.id == UUID("0f90cffa-2b6b-4e09-8fc2-527769a94e0a") - assert polygon_2d.object_id == UUID("58e7edd8-a7ee-4775-a837-e6dd375e8150") - assert polygon_2d.class_name == "test_class" - assert polygon_2d.points == [ - (127.71153737657284, -0.3861000079676791), - (127.4762636010818, 328.04436391207815), - (115.77703250958459, 334.4789410124016), - (115.01063176442402, 411.0810690770479), - ] - assert polygon_2d.attributes == attributes_uai - assert polygon_2d.sensor == sensor_camera_uai - - -def test_to_raillabel( - attributes_uai, attributes_raillabel_dict, - sensor_camera_uai, sensor_camera_raillabel_dict, coordinate_system_camera_translated_uid, -): - polygon_2d = uai_format.Polygon2d( - id=UUID("0f90cffa-2b6b-4e09-8fc2-527769a94e0a"), - object_id=UUID("58e7edd8-a7ee-4775-a837-e6dd375e8150"), - class_name="test_class", - points=[ - (127.71153737657284, -0.3861000079676791), - (127.4762636010818, 328.04436391207815), - (115.77703250958459, 334.4789410124016), - (115.01063176442402, 411.0810690770479), - ], - attributes=attributes_uai, - sensor=sensor_camera_uai, - ) - - data_dict, object_id, translated_class_id, sensor_reference = polygon_2d.to_raillabel() - - assert data_dict == { - "name": "0f90cffa-2b6b-4e09-8fc2-527769a94e0a", - "val": [ - 127.71153737657284, -0.3861000079676791, - 127.4762636010818, 328.04436391207815, - 115.77703250958459, 334.4789410124016, - 115.01063176442402, 411.0810690770479, - ], - "mode": "MODE_POLY2D_ABSOLUTE", - "coordinate_system": coordinate_system_camera_translated_uid, - "closed": True, - "attributes": attributes_raillabel_dict, - } - assert object_id == str(polygon_2d.object_id) - assert translated_class_id == translate_class_id(polygon_2d.class_name) - assert sensor_reference == sensor_camera_raillabel_dict - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_polyline_2d.py b/tests/test_raillabel/format/understand_ai/test_uai_polyline_2d.py deleted file mode 100644 index 5230c07..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_polyline_2d.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path -from uuid import UUID - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format -from raillabel.format.understand_ai._translation import translate_class_id - -# == Fixtures ========================= - -@pytest.fixture -def polyline_2d_uai_dict(sensor_camera_uai_dict, attributes_uai_dict) -> dict: - return { - "id": "7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2", - "objectId": "4d8eca35-6c1d-4159-8062-21c2f2c051df", - "className": "test_class", - "geometry": { - "points": [ - [127.71153737657284, -0.3861000079676791], - [127.4762636010818, 328.04436391207815], - [115.77703250958459, 334.4789410124016], - [115.01063176442402, 411.0810690770479], - ] - }, - "attributes": attributes_uai_dict, - "sensor": sensor_camera_uai_dict - } - - -@pytest.fixture -def polyline_2d_uai(attributes_uai, sensor_camera_uai) -> dict: - return uai_format.Polyline2d( - id=UUID("7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2"), - object_id=UUID("4d8eca35-6c1d-4159-8062-21c2f2c051df"), - class_name="test_class", - points=[ - (127.71153737657284, -0.3861000079676791), - (127.4762636010818, 328.04436391207815), - (115.77703250958459, 334.4789410124016), - (115.01063176442402, 411.0810690770479), - ], - attributes=attributes_uai, - sensor=sensor_camera_uai, - ) - -@pytest.fixture -def polyline_2d_raillabel_dict(attributes_raillabel_dict, coordinate_system_camera_translated_uid) -> dict: - return { - "name": "7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2", - "val": [ - 127.71153737657284, -0.3861000079676791, - 127.4762636010818, 328.04436391207815, - 115.77703250958459, 334.4789410124016, - 115.01063176442402, 411.0810690770479, - ], - "mode": "MODE_POLY2D_ABSOLUTE", - "closed": False, - "coordinate_system": coordinate_system_camera_translated_uid, - "attributes": attributes_raillabel_dict, - } - - -# == Tests ============================ - -def test_fromdict( - attributes_uai_dict, attributes_uai, - sensor_camera_uai_dict, sensor_camera_uai -): - polyline_2d = uai_format.Polyline2d.fromdict( - { - "id": "7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2", - "objectId": "4d8eca35-6c1d-4159-8062-21c2f2c051df", - "className": "test_class", - "geometry": { - "points": [ - [127.71153737657284, -0.3861000079676791], - [127.4762636010818, 328.04436391207815], - [115.77703250958459, 334.4789410124016], - [115.01063176442402, 411.0810690770479], - ] - }, - "attributes": attributes_uai_dict, - "sensor": sensor_camera_uai_dict - } - ) - - assert polyline_2d.id == UUID("7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2") - assert polyline_2d.object_id == UUID("4d8eca35-6c1d-4159-8062-21c2f2c051df") - assert polyline_2d.class_name == "test_class" - assert polyline_2d.points == [ - (127.71153737657284, -0.3861000079676791), - (127.4762636010818, 328.04436391207815), - (115.77703250958459, 334.4789410124016), - (115.01063176442402, 411.0810690770479), - ] - assert polyline_2d.attributes == attributes_uai - assert polyline_2d.sensor == sensor_camera_uai - - -def test_to_raillabel( - attributes_uai, attributes_raillabel_dict, - sensor_camera_uai, sensor_camera_raillabel_dict, coordinate_system_camera_translated_uid, -): - polyline_2d = uai_format.Polyline2d( - id=UUID("7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2"), - object_id=UUID("4d8eca35-6c1d-4159-8062-21c2f2c051df"), - class_name="test_class", - points=[ - (127.71153737657284, -0.3861000079676791), - (127.4762636010818, 328.04436391207815), - (115.77703250958459, 334.4789410124016), - (115.01063176442402, 411.0810690770479), - ], - attributes=attributes_uai, - sensor=sensor_camera_uai, - ) - - data_dict, object_id, translated_class_id, sensor_reference = polyline_2d.to_raillabel() - - assert data_dict == { - "name": "7f2b99b7-61e4-4f9f-96e9-d3e9f583d7c2", - "val": [ - 127.71153737657284, -0.3861000079676791, - 127.4762636010818, 328.04436391207815, - 115.77703250958459, 334.4789410124016, - 115.01063176442402, 411.0810690770479, - ], - "mode": "MODE_POLY2D_ABSOLUTE", - "coordinate_system": coordinate_system_camera_translated_uid, - "closed": False, - "attributes": attributes_raillabel_dict, - } - assert object_id == str(polyline_2d.object_id) - assert translated_class_id == translate_class_id(polyline_2d.class_name) - assert sensor_reference == sensor_camera_raillabel_dict - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_quaternion.py b/tests/test_raillabel/format/understand_ai/test_uai_quaternion.py deleted file mode 100644 index 5364632..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_quaternion.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format - -# == Fixtures ========================= - -@pytest.fixture -def quaternion_uai_dict() -> dict: - return { - "x": 0.75318325, - "y": -0.10270147, - "z": 0.21430262, - "w": -0.61338551, - } - -@pytest.fixture -def quaternion_uai() -> dict: - return uai_format.Quaternion( - x=0.75318325, - y=-0.10270147, - z=0.21430262, - w=-0.61338551, - ) - -@pytest.fixture -def quaternion_vec() -> dict: - return [0.75318325, -0.10270147, 0.21430262, -0.61338551] - -# == Tests ============================ - -def test_fromdict(): - quaternion = uai_format.Quaternion.fromdict( - { - "x": 0.75318325, - "y": -0.10270147, - "z": 0.21430262, - "w": -0.61338551, - } - ) - - assert quaternion.x == 0.75318325 - assert quaternion.y == -0.10270147 - assert quaternion.z == 0.21430262 - assert quaternion.w == -0.61338551 - - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_scene.py b/tests/test_raillabel/format/understand_ai/test_uai_scene.py deleted file mode 100644 index e5f619e..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_scene.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format -from raillabel._util._warning import _WarningsLogger - -# == Fixtures ========================= - -@pytest.fixture -def scene_uai_dict( - metadata_uai_dict, - coordinate_system_camera_uai_dict, - coordinate_system_lidar_uai_dict, - coordinate_system_radar_uai_dict, - frame_uai_dict, -) -> dict: - return { - "metadata": metadata_uai_dict, - "coordinateSystems": [ - coordinate_system_camera_uai_dict, - coordinate_system_lidar_uai_dict, - coordinate_system_radar_uai_dict, - ], - "frames": [frame_uai_dict] - } - -@pytest.fixture -def scene_uai( - metadata_uai, - coordinate_system_camera_uai, - coordinate_system_lidar_uai, - coordinate_system_radar_uai, - frame_uai, -): - return uai_format.Scene( - metadata=metadata_uai, - coordinate_systems={ - coordinate_system_camera_uai.uid: coordinate_system_camera_uai, - coordinate_system_lidar_uai.uid: coordinate_system_lidar_uai, - coordinate_system_radar_uai.uid: coordinate_system_radar_uai, - }, - frames={ - frame_uai.id: frame_uai - }, - ) - -@pytest.fixture -def scene_raillabel_dict( - metadata_raillabel_dict, - coordinate_system_camera_uai, coordinate_system_camera_raillabel_dict, - coordinate_system_lidar_uai, coordinate_system_lidar_raillabel_dict, - coordinate_system_radar_uai, coordinate_system_radar_raillabel_dict, -) -> dict: - return - -# == Tests ============================ - -def test_fromdict( - metadata_uai_dict, metadata_uai, - coordinate_system_camera_uai_dict, coordinate_system_camera_uai, - coordinate_system_lidar_uai_dict, coordinate_system_lidar_uai, - coordinate_system_radar_uai_dict, coordinate_system_radar_uai, - frame_uai_dict, frame_uai, -): - scene = uai_format.Scene.fromdict( - { - "metadata": metadata_uai_dict, - "coordinateSystems": [ - coordinate_system_camera_uai_dict, - coordinate_system_lidar_uai_dict, - coordinate_system_radar_uai_dict, - ], - "frames": [frame_uai_dict] - } - ) - - assert scene.metadata == metadata_uai - assert scene.coordinate_systems == { - coordinate_system_camera_uai.uid: coordinate_system_camera_uai, - coordinate_system_lidar_uai.uid: coordinate_system_lidar_uai, - coordinate_system_radar_uai.uid: coordinate_system_radar_uai, - } - assert scene.frames == { - frame_uai.id: frame_uai - } - -def test_fromdict_duplicate_frame_id_warning( - metadata_uai_dict, - coordinate_system_camera_uai_dict, - coordinate_system_lidar_uai_dict, - coordinate_system_radar_uai_dict, - frame_uai_dict, -): - with _WarningsLogger() as logger: - scene = uai_format.Scene.fromdict( - { - "metadata": metadata_uai_dict, - "coordinateSystems": [ - coordinate_system_camera_uai_dict, - coordinate_system_lidar_uai_dict, - coordinate_system_radar_uai_dict, - ], - "frames": [frame_uai_dict, frame_uai_dict] - } - ) - - assert len(logger.warnings) == 1 - assert "0" in logger.warnings[0] - assert len(scene.frames) == 1 - - -def test_to_raillabel__metadata(metadata_uai, metadata_raillabel_dict): - scene = uai_format.Scene( - metadata=metadata_uai, - coordinate_systems={}, - frames={}, - ) - - assert scene.to_raillabel()["openlabel"]["metadata"] == metadata_raillabel_dict - -def test_to_raillabel__sensors( - metadata_uai, - coordinate_system_camera_uai, coordinate_system_lidar_uai, - coordinate_system_camera_raillabel_dict, coordinate_system_lidar_raillabel_dict, - coordinate_system_camera_translated_uid, coordinate_system_lidar_translated_uid, -): - scene = uai_format.Scene( - metadata=metadata_uai, - coordinate_systems={ - coordinate_system_camera_uai.uid: coordinate_system_camera_uai, - coordinate_system_lidar_uai.uid: coordinate_system_lidar_uai, - }, - frames={}, - ) - - assert scene.to_raillabel()["openlabel"]["coordinate_systems"] == { - "base": { - "type": "local", - "parent": "", - "children": [ - coordinate_system_camera_translated_uid, - coordinate_system_lidar_translated_uid - ] - }, - coordinate_system_camera_translated_uid: coordinate_system_camera_raillabel_dict[0], - coordinate_system_lidar_translated_uid: coordinate_system_lidar_raillabel_dict[0], - } - assert scene.to_raillabel()["openlabel"]["streams"] == { - coordinate_system_camera_translated_uid: coordinate_system_camera_raillabel_dict[1], - coordinate_system_lidar_translated_uid: coordinate_system_lidar_raillabel_dict[1], - } - -def test_to_raillabel__frames( - metadata_uai, - coordinate_system_camera_uai, coordinate_system_lidar_uai, - frame_uai, frame_raillabel_dict -): - scene = uai_format.Scene( - metadata=metadata_uai, - coordinate_systems={ - coordinate_system_camera_uai.uid: coordinate_system_camera_uai, - coordinate_system_lidar_uai.uid: coordinate_system_lidar_uai, - }, - frames={ - frame_uai.id: frame_uai - }, - ) - - assert scene.to_raillabel()["openlabel"]["frames"] == { - str(frame_uai.id): frame_raillabel_dict - } - - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-vv"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_segmentation_3d.py b/tests/test_raillabel/format/understand_ai/test_uai_segmentation_3d.py deleted file mode 100644 index 6462506..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_segmentation_3d.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path -from uuid import UUID - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format -from raillabel.format.understand_ai._translation import translate_class_id - -# == Fixtures ========================= - -@pytest.fixture -def segmentation_3d_uai_dict(sensor_lidar_uai_dict, attributes_uai_dict) -> dict: - return { - "id": "13478f94-d556-4f64-a72b-47662e94988e", - "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", - "className": "test_class", - "geometry": { - "associatedPoints": [39814, 39815, 39816, 39817, 39818], - "numberOfPointsInBox": 5 - }, - "attributes": attributes_uai_dict, - "sensor": sensor_lidar_uai_dict, - } - -@pytest.fixture -def segmentation_3d_uai(attributes_uai, sensor_lidar_uai) -> dict: - return uai_format.Segmentation3d( - id=UUID("13478f94-d556-4f64-a72b-47662e94988e"), - object_id=UUID("05a7e7a7-91e1-49ef-a172-780f2461f013"), - class_name="test_class", - associated_points=[39814, 39815, 39816, 39817, 39818], - number_of_points=5, - attributes=attributes_uai, - sensor=sensor_lidar_uai, - ) - -@pytest.fixture -def segmentation_3d_raillabel_dict(attributes_raillabel_dict, coordinate_system_lidar_translated_uid) -> dict: - return { - "name": "13478f94-d556-4f64-a72b-47662e94988e", - "val": [39814, 39815, 39816, 39817, 39818], - "coordinate_system": coordinate_system_lidar_translated_uid, - "attributes": attributes_raillabel_dict - } - -# == Tests ============================ - -def test_fromdict( - attributes_uai_dict, attributes_uai, - sensor_lidar_uai_dict, sensor_lidar_uai -): - segmentation_3d = uai_format.Segmentation3d.fromdict( - { - "id": "13478f94-d556-4f64-a72b-47662e94988e", - "objectId": "05a7e7a7-91e1-49ef-a172-780f2461f013", - "className": "test_class", - "geometry": { - "associatedPoints": [39814, 39815, 39816, 39817, 39818], - "numberOfPointsInBox": 5 - }, - "attributes": attributes_uai_dict, - "sensor": sensor_lidar_uai_dict, - } - ) - - assert segmentation_3d.id == UUID("13478f94-d556-4f64-a72b-47662e94988e") - assert segmentation_3d.object_id == UUID("05a7e7a7-91e1-49ef-a172-780f2461f013") - assert segmentation_3d.class_name == "test_class" - assert segmentation_3d.associated_points == [39814, 39815, 39816, 39817, 39818] - assert segmentation_3d.number_of_points == 5 - assert segmentation_3d.attributes == attributes_uai - assert segmentation_3d.sensor == sensor_lidar_uai - - -def test_to_raillabel( - attributes_uai, attributes_raillabel_dict, - sensor_lidar_uai, sensor_lidar_raillabel_dict, coordinate_system_lidar_translated_uid, -): - segmentation_3d = uai_format.Segmentation3d( - id=UUID("13478f94-d556-4f64-a72b-47662e94988e"), - object_id=UUID("05a7e7a7-91e1-49ef-a172-780f2461f013"), - class_name="test_class", - associated_points=[39814, 39815, 39816, 39817, 39818], - number_of_points=5, - attributes=attributes_uai, - sensor=sensor_lidar_uai, - ) - - data_dict, object_id, translated_class_id, sensor_reference = segmentation_3d.to_raillabel() - - assert data_dict == { - "name": "13478f94-d556-4f64-a72b-47662e94988e", - "val": [39814, 39815, 39816, 39817, 39818], - "coordinate_system": coordinate_system_lidar_translated_uid, - "attributes": attributes_raillabel_dict, - } - assert object_id == str(segmentation_3d.object_id) - assert translated_class_id == translate_class_id(segmentation_3d.class_name) - assert sensor_reference == sensor_lidar_raillabel_dict - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_sensor_reference.py b/tests/test_raillabel/format/understand_ai/test_uai_sensor_reference.py deleted file mode 100644 index 0fd1f54..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_sensor_reference.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from decimal import Decimal -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format - -# == Fixtures ========================= - -@pytest.fixture -def sensor_camera_uai_dict() -> dict: - return { - "type": "ir_middle", - "uri": "A0001781_image/000_1632321843.100464760.png", - "timestamp": "1632321843.100464760" - } - -@pytest.fixture -def sensor_camera_uai() -> dict: - return uai_format.SensorReference( - type="ir_middle", - uri="A0001781_image/000_1632321843.100464760.png", - timestamp=Decimal("1632321843.100464760"), - ) - -@pytest.fixture -def sensor_camera_raillabel_dict() -> dict: - return { - "stream_properties": { - "sync": { - "timestamp": "1632321843.100464760" - } - }, - "uri": "000_1632321843.100464760.png" - } - - -@pytest.fixture -def sensor_lidar_uai_dict() -> dict: - return { - "type": "LIDAR", - "uri": "lidar_merged/000_1632321880.132833000.pcd", - "timestamp": "1632321880.132833000" - } - -@pytest.fixture -def sensor_lidar_uai() -> dict: - return uai_format.SensorReference( - type="LIDAR", - uri="lidar_merged/000_1632321880.132833000.pcd", - timestamp=Decimal("1632321880.132833000"), - ) - -@pytest.fixture -def sensor_lidar_raillabel_dict() -> dict: - return { - "stream_properties": { - "sync": { - "timestamp": "1632321880.132833000" - } - }, - "uri": "000_1632321880.132833000.pcd" - } - -# == Tests ============================ - -def test_fromdict(): - sensor_reference = uai_format.SensorReference.fromdict( - { - "type": "ir_middle", - "uri": "A0001781_image/000_1632321843.100464760.png", - "timestamp": "1632321843.100464760" - } - ) - - assert sensor_reference.type == "ir_middle" - assert sensor_reference.uri == "A0001781_image/000_1632321843.100464760.png" - assert sensor_reference.timestamp == Decimal("1632321843.100464760") - - -def test_to_raillabel(): - sensor_reference = uai_format.SensorReference( - type="ir_middle", - uri="A0001781_image/000_1632321843.100464760.png", - timestamp=Decimal("1632321843.100464760"), - ) - - assert sensor_reference.to_raillabel()[0] == "ir_middle" - assert sensor_reference.to_raillabel()[1] == { - "stream_properties": { - "sync": { - "timestamp": "1632321843.100464760" - } - }, - "uri": "000_1632321843.100464760.png" - } - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/format/understand_ai/test_uai_size_3d.py b/tests/test_raillabel/format/understand_ai/test_uai_size_3d.py deleted file mode 100644 index 0734b03..0000000 --- a/tests/test_raillabel/format/understand_ai/test_uai_size_3d.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel.format.understand_ai as uai_format - -# == Fixtures ========================= - -@pytest.fixture -def size_3d_uai_dict() -> dict: - return { - "width": 3, - "length": 4, - "height": 5, - } - -@pytest.fixture -def size_3d_uai() -> dict: - return uai_format.Size3d( - width=3, - length=4, - height=5, - ) - -@pytest.fixture -def size_3d_vec() -> dict: - return [3, 4, 5] - -# == Tests ============================ - -def test_fromdict(): - size_3d = uai_format.Size3d.fromdict( - { - "width": 3, - "length": 4, - "height": 5, - } - ) - - assert size_3d.width == 3 - assert size_3d.length == 4 - assert size_3d.height == 5 - - -if __name__ == "__main__": - import os - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/load_/loader_classes/test_loader_raillabel.py b/tests/test_raillabel/load_/loader_classes/test_loader_raillabel.py deleted file mode 100644 index 01423a4..0000000 --- a/tests/test_raillabel/load_/loader_classes/test_loader_raillabel.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel - - -@pytest.fixture -def loader(): - return raillabel.load_.loader_classes.LoaderRailLabel() - - -def test_supports_true(json_data, loader): - assert loader.supports(json_data["openlabel_v1_short"]) - - -def test_supports_false(json_data, loader): - data = json_data["openlabel_v1_short"] - data["openlabel"]["metadata"]["subschema_version"] = "4.0.0" - assert not loader.supports(data) - - -# Tests the warnings and errors -def test_no_warnings(json_data, loader): - loader.load(json_data["openlabel_v1_short"], validate=False) - assert len(loader.warnings) == 0 - - -def test_warnings_sync(json_data, loader): - data = json_data["openlabel_v1_short"] - - data["openlabel"]["frames"]["0"]["frame_properties"]["streams"]["non_existing_stream"] = { - "stream_properties": { - "sync": { - "timestamp": "1632321743.100000072" - } - } - } - - loader.load(data, validate=False) - assert len(loader.warnings) == 1 - - # Tests for keywords in the warning that can help the user identify the source - assert "frame" in loader.warnings[0] - assert "0" in loader.warnings[0] - assert "sync" in loader.warnings[0] - assert "non_existing_stream" in loader.warnings[0] - - -def test_warnings_stream_sync_field(json_data, loader): - data = json_data["openlabel_v1_short"] - - data["openlabel"]["frames"]["0"]["frame_properties"]["streams"][ - "rgb_middle" - ]["stream_properties"]["stream_sync"] = data["openlabel"]["frames"]["0"][ - "frame_properties" - ][ - "streams" - ][ - "rgb_middle" - ][ - "stream_properties" - ][ - "sync" - ] - del data["openlabel"]["frames"]["0"]["frame_properties"]["streams"][ - "rgb_middle" - ]["stream_properties"]["sync"] - - loader.load(data, validate=False) - assert len(loader.warnings) == 1 - - # Tests for keywords in the warning that can help the user identify the source - assert "stream_sync" in loader.warnings[0] - assert "deprecated" in loader.warnings[0].lower() - assert "save()" in loader.warnings[0] - - -def test_identify_of_references(json_data, loader): - data = json_data["openlabel_v1_short"] - - scene = loader.load(data, validate=False) - - for frame in scene.frames.values(): - - for sensor_reference in frame.sensors.values(): - assert sensor_reference.sensor is scene.sensors[sensor_reference.sensor.uid] - - for frame_data in frame.frame_data.values(): - assert frame_data.sensor is scene.sensors[frame_data.sensor.uid] - - for annotation in frame.annotations.values(): - assert annotation.sensor is scene.sensors[annotation.sensor.uid] - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/load_/loader_classes/test_loader_understand_ai.py b/tests/test_raillabel/load_/loader_classes/test_loader_understand_ai.py deleted file mode 100644 index 293e5f3..0000000 --- a/tests/test_raillabel/load_/loader_classes/test_loader_understand_ai.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent.parent)) - -import raillabel - - -@pytest.fixture -def loader(): - return raillabel.load_.loader_classes.LoaderUnderstandAi() - - -def test_supports_true(json_data, loader): - assert loader.supports(json_data["understand_ai_real_life"]) - - -def test_supports_false(json_data, loader): - data = json_data["understand_ai_real_life"] - del data["metadata"]["project_id"] - assert not loader.supports(data) - - -def test_load(json_data, loader): - input_data_raillabel = remove_non_parsed_fields(json_data["openlabel_v1_short"]) - input_data_uai = json_data["understand_ai_t4_short"] - - scene_ground_truth = raillabel.load_.loader_classes.LoaderRailLabel().load(input_data_raillabel, validate=False) - scene = loader.load(input_data_uai, validate=False) - - scene.metadata = scene_ground_truth.metadata - - assert scene.asdict() == scene_ground_truth.asdict() - -def remove_non_parsed_fields(raillabel_data: dict) -> dict: - """Return RailLabel file with frame_data and poly3ds removed.""" - - for frame in raillabel_data["openlabel"]["frames"].values(): - - if "frame_data" in frame["frame_properties"]: - del frame["frame_properties"]["frame_data"] - - for object_id, object in list(frame["objects"].items()): - if "poly3d" not in object["object_data"]: - continue - - del object["object_data"]["poly3d"] - if len(object["object_data"]) == 0: - del frame["objects"][object_id] - - return raillabel_data - - -def test_raillabel_loader_warnings(loader): - scene_dict = { - "metadata": { - "clip_id": "db_3_2021-09-22-14-28-01_2021-09-22-14-44-03", - "external_clip_id": "2021-09-22-14-28-01_2021-09-22-14-44-03", - "project_id": "trains_4", - "export_time": "2023-04-20 01:38 UTC", - "exporter_version": "1.0.0", - "coordinate_system_3d": "FLU", - "coordinate_system_reference": "SENSOR", - "folder_name": "2021-09-22-14-28-01_2021-09-22-14-44-03" - }, - "coordinateSystems": [], - "frames": [ - { - "frameId": "000", - "timestamp": "1632321743.134149", - "annotations": { - "2D_BOUNDING_BOX": [ - { - "id": "78f0ad89-2750-4a30-9d66-44c9da73a714", - "objectId": "b40ba3ad-0327-46ff-9c28-2506cfd6d934", - "className": "2D_person", - "geometry": { - "xMin": -1.0, - "yMin": -0.5, - "xMax": 1, - "yMax": 2.5 - }, - "attributes": {}, - "sensor": { - "type": "NON_EXISTENT_SENSOR", # <-- relevant line - "uri": "S1206063/rgb_test0.png", - "timestamp": "1632321743.100000072" - } - } - ], - "2D_POLYLINE": [], - "2D_POLYGON": [], - "3D_BOUNDING_BOX": [], - "3D_SEGMENTATION": [] - } - } - ] - } - - loader.load(scene_dict, validate=True) - - assert len(loader.warnings) == 2 - - assert "NON_EXISTENT_SENSOR" in loader.warnings[0] - assert "frame 0" in loader.warnings[0] - - assert "NON_EXISTENT_SENSOR" in loader.warnings[1] - assert "78f0ad89-2750-4a30-9d66-44c9da73a714" in loader.warnings[1] - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/load_/test_load.py b/tests/test_raillabel/load_/test_load.py deleted file mode 100644 index f929565..0000000 --- a/tests/test_raillabel/load_/test_load.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent)) - -import raillabel - - -def test_load_raillabel(json_paths): - data_path = json_paths["openlabel_v1_short"] - scene = raillabel.load(data_path) - assert len(scene.frames) != 0 - - -def test_load_uai(json_paths): - data_path = json_paths["understand_ai_t4_short"] - scene = raillabel.load(data_path) - assert len(scene.frames) != 0 - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/save/test_save.py b/tests/test_raillabel/save/test_save.py deleted file mode 100644 index 93bb32c..0000000 --- a/tests/test_raillabel/save/test_save.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import json -import os -import sys -import tempfile -from copy import deepcopy -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent)) - -import raillabel - - -def test_save_scene(json_paths): - with tempfile.TemporaryDirectory("w") as temp_dir: - - scene_orig = raillabel.load(json_paths["openlabel_v1_short"], False, False) - - raillabel.save(scene_orig, Path(temp_dir) / "test_save_file.json") - scene_saved = raillabel.load(Path(temp_dir) / "test_save_file.json", False, False) - - assert scene_orig == scene_saved - - -def test_save_json(json_data): - with tempfile.TemporaryDirectory("w") as temp_dir: - - stripped_input_data = deepcopy(json_data["openlabel_v1_short"]) - - # Removes the object data pointers from the example file so that it needs to be generated from the data - for object in stripped_input_data["openlabel"]["objects"].values(): - del object["frame_intervals"] - del object["object_data_pointers"] - - with (Path(temp_dir) / "stripped_input_data.json").open("w") as f: - json.dump(stripped_input_data, f) - - scene = raillabel.load(Path(temp_dir) / "stripped_input_data.json", False, False) - raillabel.save(scene, Path(temp_dir) / "test_save_file.json") - - with (Path(temp_dir) / "test_save_file.json").open() as f: - saved_and_loaded_data = json.load(f) - - # Removes the exporter version from the generated file as these are hard to test for - if "exporter_version" in saved_and_loaded_data["openlabel"]["metadata"]: - del saved_and_loaded_data["openlabel"]["metadata"]["exporter_version"] - - assert saved_and_loaded_data == json_data["openlabel_v1_short"] - - -def test_frame_intervals(): - data = { - "openlabel": { - "metadata": {"schema_version": "1.0.0"}, - "frames": { - "0": {}, - "1": {}, - "2": {}, - "5": {}, - "7": {}, - "8": {}, - }, - } - } - - scene = raillabel.load_.loader_classes.LoaderRailLabel().load(data) - dict_repr = scene.asdict()["openlabel"] - - assert "frame_intervals" in dict_repr - assert dict_repr["frame_intervals"] == [ - {"frame_start": 0, "frame_end": 2}, - {"frame_start": 5, "frame_end": 5}, - {"frame_start": 7, "frame_end": 8}, - ] - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/stats/test_generate_timespan.py b/tests/test_raillabel/stats/test_generate_timespan.py deleted file mode 100644 index a9e0236..0000000 --- a/tests/test_raillabel/stats/test_generate_timespan.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent)) - -import raillabel - -# == Fixtures ============================ - -@pytest.fixture -def metadata() -> raillabel.format.Metadata: - return raillabel.format.Metadata(schema_version="1.0.0") - -# == Tests ============================ - -def test_simple_timespan(metadata): - scene = raillabel.Scene( - metadata=metadata, - frames={ - 0: raillabel.format.Frame( - uid=0, - timestamp=100 - ), - 1: raillabel.format.Frame( - uid=1, - timestamp=105 - ), - 2: raillabel.format.Frame( - uid=0, - timestamp=110 - ), - } - ) - - assert raillabel.stats.generate_timespan(scene) == (100, 110) - -def test_unordered_timspan(metadata): - scene = raillabel.Scene( - metadata=metadata, - frames={ - 0: raillabel.format.Frame( - uid=0, - timestamp=110 - ), - 1: raillabel.format.Frame( - uid=1, - timestamp=100 - ), - } - ) - - assert raillabel.stats.generate_timespan(scene) == (100, 110) - -def test_empty_timespan(metadata): - scene = raillabel.Scene( - metadata=metadata, - frames={} - ) - - assert raillabel.stats.generate_timespan(scene) == (None, None) - - -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear", "-v"]) diff --git a/tests/test_raillabel/validate/schemas/test_raillabel_v2_schema.py b/tests/test_raillabel/validate/schemas/test_raillabel_v2_schema.py deleted file mode 100644 index 33ae8bd..0000000 --- a/tests/test_raillabel/validate/schemas/test_raillabel_v2_schema.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os - -import jsonschema -import pytest - - -def test_metaschema_validation(json_data): - assert jsonschema.validate( - json_data["raillabel_schema"], - json_data["metaschema"] - ) is None - - -def test_sample_data_validation_subschema(json_data): - assert jsonschema.validate( - json_data["openlabel_v1_short"], - json_data["raillabel_schema"] - ) is None - - -def test_sample_data_validation_superschema(json_data): - assert jsonschema.validate( - json_data["openlabel_v1_short"], - json_data["openlabel_v1_schema"] - ) is None - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/validate/schemas/test_understand_ai_t4_schema.py b/tests/test_raillabel/validate/schemas/test_understand_ai_t4_schema.py deleted file mode 100644 index 17af7dc..0000000 --- a/tests/test_raillabel/validate/schemas/test_understand_ai_t4_schema.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os - -import jsonschema -import pytest - - -def test_metaschema_validation(json_data): - assert jsonschema.validate( - json_data["understand_ai_t4_schema"], - json_data["metaschema"] - ) is None - - -def test_sample_data_validation_subschema(json_data): - assert jsonschema.validate( - json_data["understand_ai_real_life"], - json_data["understand_ai_t4_schema"] - ) is None - - assert jsonschema.validate( - json_data["understand_ai_t4_short"], - json_data["understand_ai_t4_schema"] - ) is None - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"]) diff --git a/tests/test_raillabel/validate/test_validate.py b/tests/test_raillabel/validate/test_validate.py deleted file mode 100644 index b22b28d..0000000 --- a/tests/test_raillabel/validate/test_validate.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -from pathlib import Path - -import pytest - -sys.path.insert(1, str(Path(__file__).parent.parent.parent.parent)) - -import raillabel - - -def test_valid_file(json_data): - validation_result = raillabel.validate(json_data["openlabel_v1_short"]) - assert validation_result[0] - - -def test_file_one_type_error(json_data): - data = json_data["openlabel_v1_short"] - - data["openlabel"]["streams"]["lidar"]["uri"] = 42 - - validation_result = raillabel.validate(data) - - assert not validation_result[0] and len(validation_result[1]) == 1 - - -def test_file_two_type_errors(json_data): - data = json_data["openlabel_v1_short"] - - data["openlabel"]["streams"]["lidar"]["uri"] = 42 - data["openlabel"]["coordinate_systems"]["base"]["type"] = "invalid_value" - - validation_result = raillabel.validate(data) - - assert not validation_result[0] and len(validation_result[1]) == 2 - - -def test_valid_file_path(json_data, json_paths): - validation_result = raillabel.validate(json_data["openlabel_v1_short"], str(json_paths["raillabel_schema"])) - assert validation_result[0] - - -def test_invalid_file_path(json_data, json_paths): - json_paths["raillabel_schema"] = str(json_paths["raillabel_schema"]) + "_invalid" - - with pytest.raises(FileNotFoundError): - raillabel.validate(json_data["openlabel_v1_short"], json_paths["raillabel_schema"]) - - -def test_invalid_schema_key(json_data): - with pytest.raises(FileNotFoundError): - raillabel.validate(json_data["openlabel_v1_short"], "invalid_schema") - - -# Executes the test if the file is called -if __name__ == "__main__": - os.system("clear") - pytest.main([__file__, "--disable-pytest-warnings", "--cache-clear"])