---
title: OpenLABEL 형식
slug: api-guide/ko/openlabel
docTags: 
createdAt: 2024-12-17T12:57:39.000Z
---

OpenLABEL은 [ASAM](https://www.asam.net/)이 개발한 표준화된 어노테이션 형식입니다. 이 형식에 대한 자세한 [문서](https://www.asam.net/index.php?eID=dumpFile\&t=f\&f=4566\&token=9d976f840af04adee33b9f85aa3c22f2de4968dd)와, 형식이 유효한지 확인하는 데 사용할 수 있는 [json 스키마](https://openlabel.asam.net/V1-0-0/schema/openlabel_json_schema.json)가 제공됩니다.

OpenLABEL은 엄격한 형식이지만 여전히 해석의 여지가 있는 부분들이 있습니다. 이 섹션에서는 이러한 부분 중 일부를 명확히 하고, 당사가 표준 내에서 내린 선택들을 설명합니다.

## 큐보이드의 회전

회전은 y축이 전방을 향하도록 되어 있으며, 회전 순서는 `XYZ`입니다.
즉, 헤딩(요, yaw)이 0인 큐보이드는 y축의 양의 방향과 정렬됩니다. 이는 전방이 x축을 따르는 [ISO 8855](https://www.sis.se/api/document/preview/914200/#:~\:text=This%20International%20Standard%20defines%20the,to%20multi%2Dunit%20vehicle%20combinations.) 표준과는 다소 차이가 있습니다. ISO 8855로의 변환은 z축을 기준으로 회전을 적용하고 다음과 같이 `sx`와 `sy`를 변경함으로써 수행할 수 있습니다.

```python
import math 
from typing import List

from scipy.spatial.transform import Rotation

def convert_to_iso8855(val: List[float]) -> List[float]:
    """ Converts cuboid values to ISO 8855 """
    [x, y, z, qx, qy, qz, qw, sx, sy, sz] = val
    rotation_1 = Rotation.from_quat([qx, qy, qz, qw])
    rotation_2 = Rotation.from_rotvec([0, 0, math.pi / 2])
    rot_object = rotation_1 * rotation_2
    [qx, qy, qz, qw] = rot_object.as_quat()
    return [x, y, z, qx, qy, qz, qw, sy, sx, sz]
```

## 시퀀스가 아닌 데이터는 프레임이 하나인 시퀀스입니다

단순성을 위해, 시퀀스가 아닌 데이터도 시퀀스와 동일한 방식으로 처리하기로 결정했습니다. 즉, 시퀀스가 아닌 데이터는 프레임이 하나뿐인 시퀀스로 표현됩니다. `name`이나 `type`과 같은 데이터만 최상위 요소 키에 정의됩니다. 그 외의 모든 정보는 frames 아래에 저장됩니다. 아래 예제를 참고하세요.

```json
{
    "objects": {
        "0": {
            "name": "car-0",
            "type": "Car"
        }
    },
    "frames": {
        "0": {
            "objects": {
                "0": {"object_data": {...}}
            }
        }
    }
}
```

## 스트림은 일반 텍스트 속성입니다

`stream` 속성은 지오메트리와 속성이 어노테이션된 스트림/센서/소스가 무엇인지 나타내는 데 사용됩니다. 예를 들어 다음은 `Camera`라는 이름의 스트림에서 어노테이션된 포인트(point)를 가진 객체입니다. 해당 지오메트리에 대응하는 모든 속성 역시 동일한 스트림에서 어노테이션되었다는 점에 유의하세요.

```json
{
    "object_data": {
        "point2d": [
            {
                "name": "point-4d2d325f",
                "val": [300.5300, 286.4396],
                "attributes": {
                    "text": [
                        {"name": "stream", "val": "Camera"},
                        {"name": "Color", "val": "Black"}
                    ]
                }
            }
        ]
    }
}
```

## 관계(Relations)

관계에는 두 가지 유형이 있습니다. 두 객체 간의 단방향 관계와, 그룹 관계입니다. 이 외에도, 실제로는 다른 객체를 가리키는 포인터가 아니라 `Inconclusive`, `Nothing`, `Unclear`와 같은 값을 가지는 거짓 관계(false relation) 속성을 표현할 필요도 있습니다.

### 관계는 단방향입니다

관계는 단방향(unidirectional)이며, 즉 객체 `object1`이 다른 객체 `object2`에 대한 관계를 가진다고 해서 `object2`가 `object1`에 대한 관계를 가지는 것은 아닙니다. 아래는 `car-0`이 `car-1`을 따라가고 있으며, `car-2`가 다른 차를 따라가고 있는지는 불명확한 예제입니다.

```json
{
    "objects": {
        "0": {"name": "car-0", "type": "Car"},
        "1": {"name": "car-1", "type": "Car"},
        "2": {"name": "car-2", "type": "Car"}
    },
    "relations": {
        "0": {
            "name": "0",
            "type": "isFollowing",
            "rdf_subjects": [{"type": "object", "uid": "0"}],
            "rdf_objects": [{"type": "object", "uid": "1"}]
        }
    }
}
```

### 액션(Actions)은 거짓 관계를 표현하는 데 사용됩니다

Kognic 플랫폼에서는 실제로는 다른 객체에 대한 참조가 아닌 값을 관계에 할당하는 기능을 지원합니다. `Inconclusive`와 `Nothing`이 그 예입니다. 이러한 값은 다음과 같이 액션(actions)을 사용해 표현하며, 액션의 이름이 값을 결정하고 타입이 속성 이름을 결정합니다.

```json
{
    "objects": {
        "0": {"name": "lane-0", "type": "Lane"}
    },
    "relations": {
        "0": {
            "name": "0",
            "type": "isSubjectOfAction",
            "rdf_subjects": [{"type": "object", "uid": "0"}],
            "rdf_objects": [{"type": "action", "uid": "0"}]
        }
    },
    "actions": {
        "0": {"name": "Nothing", "type": "is_pulling_or_pushing"}
    }
}
```

### 스트림별 관계

어떤 관계가 특정 스트림에 국한된 경우, 해당 관계 목록이 어느 스트림에 속하는지를 나타내는 `stream_relations` 속성이 있게 됩니다.

```json
{ // frames.0
    // ...
    "frame_properties": {
        "streams": {
            "CAMERA_FRONT": {
                "description": null,
                "stream_properties": {
                    "stream_relations": {
                        "1": {}
                    }
                }
            }
        }
    },
    "relations": {
        "0": {}
    }
}
```

## 폴리곤 표현하기

OpenLABEL에서 폴리곤은 `Poly2d` 객체 목록으로 기술됩니다. 이 중 하나는 외곽선(exterior)을 나타내고, 나머지는 잠재적인 구멍(hole)을 나타내며, 이는 불리언 속성 `is_hole`로 결정됩니다. 아래는 구멍이 하나 있는 폴리곤의 예제입니다.

```json
{
    "object_data": {
        "poly2d": [
            {
                "name": "poly1",
                "mode": "MODE_POLY2D_ABSOLUTE",
                "val": [...],
                "attributes": { "boolean": [{"name": "is_hole", "val": false}] }
            },
            {
                "name": "poly2",
                "mode": "MODE_POLY2D_ABSOLUTE",
                "val": [...],
                "attributes": { "boolean": [{"name": "is_hole", "val": true}] }
            }
        ]
    }
}
```

`mode`로 지원되는 값은 `MODE_POLY2D_ABSOLUTE`뿐입니다. 절대(absolute) 모드는 `val`의 값들이 (첫 번째 좌표쌍에 대한 상대값이 아니라) 픽셀 좌표로 해석됨을 의미합니다.

## 멀티 폴리곤 표현하기

멀티 폴리곤은 단순히 폴리곤들의 목록이므로, `is_hole` 속성을 가진 `Poly2d` 객체 목록을 사용해 유사한 방식으로 기술합니다. 다만, 멀티 폴리곤 내에서 `Poly2d` 객체가 어느 폴리곤에 속하는지를 결정하는 `polygon_id`라는 속성을 하나 더 추가합니다. 아래는 각각 하나의 구멍을 가진 두 개의 폴리곤으로 구성된 멀티 폴리곤 예제입니다.

```json
{
    "object_data": {
        "poly2d": [
            {
                "name": "poly1",
                "mode": "MODE_POLY2D_ABSOLUTE",
                "val": [...],
                "attributes": {
                    "text": [{"name": "polygon_id", "val": "1"}],
                    "boolean": [{"name": "is_hole", "val": false}]
                }
            },
            {
                "name": "poly2",
                "mode": "MODE_POLY2D_ABSOLUTE",
                "val": [...],
                "attributes": {
                    "text": [{"name": "polygon_id", "val": "1"}],
                    "boolean": [{"name": "is_hole", "val": true}]
                }
            },
            {
                "name": "poly3",
                "mode": "MODE_POLY2D_ABSOLUTE",
                "val": [...],
                "attributes": {
                    "text": [{"name": "polygon_id", "val": "2"}],
                    "boolean": [{"name": "is_hole", "val": false}]
                }
            },
            {
                "name": "poly4",
                "mode": "MODE_POLY2D_ABSOLUTE",
                "val": [...],
                "attributes": {
                    "text": [{"name": "polygon_id", "val": "2"}],
                    "boolean": [{"name": "is_hole", "val": true}]
                }
            }
        ]
    }
}
```

`mode`로 지원되는 값은 `MODE_POLY2D_ABSOLUTE`뿐입니다. 절대(absolute) 모드는 `val`의 값들이 (첫 번째 좌표쌍에 대한 상대값이 아니라) 픽셀 좌표로 해석됨을 의미합니다.

## 곡선 표현하기

:::hint{type="warning"}
### 주의

보간(interpolation) 방식의 이름이 `interpolation-method`에서 `interpolation_method`로 변경되었습니다. 다만 기존 어노테이션에는 이전 이름이 여전히 남아 있을 수 있습니다.
:::

곡선은 `poly2d` 지오메트리를 사용해 표현되며, 보간 방식은 다음과 같이 텍스트 속성으로 지정됩니다.

```json
{
  "poly2d": [
    {
      "closed": false,
      "mode": "MODE_POLY2D_ABSOLUTE",
      "name": "curve-d633ca89",
      "val": [...],
      "attributes": {
        "text": [
          {
            "name": "interpolation_method",
            "val": "natural-cubic-spline"
          }
        ]
      }
    }
  ]
}
```

`mode`로 지원되는 값은 `MODE_POLY2D_ABSOLUTE`뿐입니다. 절대(absolute) 모드는 `val`의 값들이 (첫 번째 좌표쌍에 대한 상대값이 아니라) 픽셀 좌표로 해석됨을 의미합니다. `interpolation_method` 속성은 필수이며, 노드들이 서로 어떻게 연결되어야 하는지를 결정합니다. 다음 값들이 지원됩니다.

- `natural-cubic-spline`
- `catmull-rom-0.5`
- `polyline`

## 3D 차선(Lane) 표현하기

3D 차선은 3D 상의 두 개의 라인(`poly3d`)으로 표현되며, 하나는 오른쪽, 다른 하나는 왼쪽을 나타냅니다. 텍스트 속성 `lane_edge`가 해당 라인이 오른쪽인지 왼쪽인지를 결정합니다. 이 라인들은 항상 closed 값이 false로 설정됩니다.

```json
{
  "object_data": {
    "poly3d": [
      {
        "attributes": {
          "text": [
            { "name": "lane_edge", "val": "left" },
            { "name": "stream", "val": "lidar" }
          ]
        },
        "closed": false,
        "name": "",
        "val": [
          1.2647494200238287, -51.51747573498745, -2.315540290283199,
          1.0807419132566136, -48.91298533071834, -2.313640304199211,
          -0.0892715141237751, -34.705936676401016, -2.235569814758307,
          -0.4442893388935316, -29.60917111552865, -2.1894531147766174,
          -1.0952988968721313, -17.193981050037397, -2.1397902661132875
        ]
      },
      {
        "attributes": {
          "text": [
            { "name": "lane_edge", "val": "right" },
            { "name": "stream", "val": "lidar" }
          ]
        },
        "closed": false,
        "name": "",
        "val": [
          1.5845765823868767, -51.49487958011918, -2.315540290283199,
          1.4004322100638888, -48.888528958803036, -2.313640304199211,
          0.23043085215069048, -34.68163859008775, -2.235569814758307,
          -0.12426061849402326, -29.589636067040036, -2.1894531147766174
        ]
      }
    ]
  }
}
```

## 2D 포인트 표현하기

2D 포인트는 하나의 point2d로 표현됩니다. 각 `point2d`는 선택적인 `point_class` 속성을 가질 수 있습니다. 단일 포인트의 경우 이를 생략할 수 있지만, 설정하는 경우 반드시 해당 객체의 `type`과 동일해야 합니다. 이 속성은 향후 다른 포인트 기반 지오메트리에서 사용하기 위해 예약되어 있습니다.

```json
{
  "openlabel": {
    ...,
    "frames": {
      "0": {
        "objects": {
          "a940239d-ff27-4480-8294-c482977a1b32": {
            "object_data": {
              "point2d": [
                {
                  "attributes": {
                    "text": [
                      { "name": "point_class", "val": "APoint" },
                      { "name": "stream", "val": "stream1" }
                    ]
                  },
                  ...
                }
              ]
            }
          },
          "e027e626-eb7a-4a8e-a9ae-083464e137d1": {
            "object_data": {
              "point2d": [
                {
                  "attributes": {
                    "text": [
                      { "name": "stream", "val": "stream1" }
                    ]
                  },
                  ....
                }
              ]
            }
          }
        }
      }
    },
    "metadata": {...},
    "objects": {
      "a940239d-ff27-4480-8294-c482977a1b32": {
        ...
        "type": "APoint"
      },
      "e027e626-eb7a-4a8e-a9ae-083464e137d1": {
        ...
        "type": "AnotherPoint"
      }
    },
    ...
  }
}

```

## 2D 포인트 그룹 표현하기

여러 포인트가 동일한 객체를 가리킬 때 포인트 그룹(group of points)을 사용합니다.
포인트 그룹 내 각 포인트에는 `point_class` 속성이 필수이며, `point_class`는 객체 타입과는 달라야 합니다. `point_class` 값 `"line_reference_point"`는 향후 사용 사례를 위해 예약되어 있습니다.

## 지오메트리 컬렉션(Geometry Collections) 표현하기

:::hint{type="info"}
### 참고

`kognic_format_version` 2.2에서 도입되었습니다.
:::

작업 화면(task view)에 대한 관련 문서 [지오메트리 컬렉션(Geometry collections)](#).

위 링크에서 설명한 지오메트리 컬렉션은 타입이 `geometry_collection`인 예약된 관계로 표현됩니다.

```json
{
  "openlabel": {
    "frames": {
      "0": {
        "objects": {
          "516b6045-87e8-40e4-a104-5eaa600e8e3a": {
            "object_data": {
              "bbox": [
                {
                  "name": "bbox-abc123",
                  "val": [
                    ...
                  ]
                }
              ]
            }
          },
          "fe07e9cf-f42c-4b48-b4d8-bab75b7e9827": {
            "object_data": {
              "poly2d": [
                {
                  "name": "curve-abc123",
                  "val": [
                    ...
                  ]
                }
              ]
            }
          },
          "329508b7-729c-4298-8141-f329dbc32ad0": {
            "object_data": {
              "poly2d": [
                {
                  "name": "curve-abc123",
                  "val": [
                    ...
                  ]
                }
              ]
            }
          },
          "4c321584-0e88-4578-b0f0-b5e8c974244b": {
            "object_data": {
              "text": [
                {
                  "name": "lane",
                  "val": "right"
                }
              ]
            }
          }
        }
      }
    },
    "metadata": {
      "schema_version": "1.0.0",
      "kognic_format_version": "2.2",
      "uuid": "63698712-b18e-426b-9ad5-1b178cc29838"
    },
    "objects": {
      "516b6045-87e8-40e4-a104-5eaa600e8e3a": {
        "name": "516b6045-87e8-40e4-a104-5eaa600e8e3a",
        "object_data": {
            ...
        },
        "type": "some_bbox"
      },
      "fe07e9cf-f42c-4b48-b4d8-bab75b7e9827": {
        "name": "fe07e9cf-f42c-4b48-b4d8-bab75b7e9827",
        "object_data": {
            ...
        },
        "type": "some_line"
      },
      "329508b7-729c-4298-8141-f329dbc32ad0": {
        "name": "329508b7-729c-4298-8141-f329dbc32ad0",
        "object_data": {
            ...
        },
        "type": "some_line"
      },
      "4c321584-0e88-4578-b0f0-b5e8c974244b": {
        "name": "4c321584-0e88-4578-b0f0-b5e8c974244b",
        "object_data": {
            ...
        },
        "type": "some_collection"
      }
    },
    "relations": {
      "0": {
        "name": "0",
        "rdf_objects": [
          {
            "uid": "516b6045-87e8-40e4-a104-5eaa600e8e3a",
            "type": "object"
          },
          {
            "uid": "fe07e9cf-f42c-4b48-b4d8-bab75b7e9827",
            "type": "object"
          },
          {
            "uid": "329508b7-729c-4298-8141-f329dbc32ad0",
            "type": "object"
          }
        ],
        "rdf_subjects": [
          {
            "uid": "4c321584-0e88-4578-b0f0-b5e8c974244b",
            "type": "object"
          }
        ],
        "type": "geometry_collection"
      }
    }
  }
}
```

## 3D 세그멘테이션 표현하기

분류(classification)는 값을 런렝스 인코딩(run length encoding, RLE)하여 표현됩니다. 예를 들어 11122222222000000000000은 #3V1#8V2#13V0으로 변환되며, 여기서 `#` 문자 뒤의 숫자는 반복되는 값의 개수를 나타내고, `V` 문자 뒤의 숫자가 해당 값을 나타냅니다.

```json
"b088dcbf-c7fe-46a5-a71d-88ef49bdc107": {
  "object_data": {
    "binary": [
      {
        "attributes": {
          "text": [
            {
              "val": "lidar",
              "name": "stream"
            }
          ]
        },
        "data_type": "",
        "encoding": "rle",
        "name": "labels",
        "val": "<RLE string>"
      }
    ]
  }
}
```
