---
title: 개요
slug: api-guide/ko/BnENKgYR7_8igj7gNfbgL
docTags: 
createdAt: 2024-12-17T10:27:12.000Z
---

## 씬의 다양한 타입

씬은 함께 어노테이션되어야 하는 센서 데이터(예: 카메라 이미지, 라이다 포인트 클라우드)의 그룹을 나타냅니다. 센서와 그 캡처된 데이터 사이의 관계를 설명하는 데 필요한 정보, 즉 카메라 해상도, 센서 이름, 데이터가 기록된 빈도 등도 씬에 함께 명시됩니다.

씬의 내용을 표현하는 데 사용되는 센서의 종류에 따라 다양한 씬 타입이 존재합니다. 예를 들어 카메라 센서의 이미지 데이터만으로 씬을 만들고 싶다면 씬 타입 `Cameras`를 사용합니다. 마찬가지로 라이다와 카메라 센서를 모두 포함하는 씬을 만들고 싶다면 씬 타입 `LidarsAndCameras`를 사용합니다. 또한 씬은 *단일 프레임(single frame)* 타입이거나 *시퀀스(sequence)* 타입일 수 있습니다.

### 시퀀스형 vs 비시퀀스형

시퀀스형 씬은 시간에 따른 프레임의 *시퀀스*를 나타내는 반면, 비시퀀스형 씬은 센서 데이터의 스냅샷 하나만을 포함합니다. 시퀀스 관계는 **Frame**의 시퀀스를 통해 표현되며, 각 Frame은 프레임을 구성하는 센서 데이터의 종류(예: 프레임에 포함된 이미지 및/또는 포인트 클라우드)에 대한 정보와, 다른 프레임을 기준으로 해당 Frame이 시간상 어디에 위치하는지를 나타내는 *상대 타임스탬프*(relative timestamp)를 포함합니다.

비시퀀스형 씬은 단일 Frame만 포함하며 상대 타임스탬프 정보가 필요하지 않습니다.

시퀀스형 씬 타입은 타입 이름에 `Seq` 접미사가 붙어 식별됩니다.

현재 다음 씬 타입이 지원됩니다.

- `Cameras`
- `LidarsAndCameras`
- `CamerasSeq`
- `LidarsAndCamerasSeq`
- `AggregatedLidarsAndCamerasSeq`

## 씬 필드

비시퀀스형 씬은 다음과 같은 구조를 가집니다.

```python
class Scene(BaseModel):
    external_id: str
    frame: Frame
    sensor_specification: SensorSpecification
    calibration_id: Optional[str] # Required if using lidar sensors
    metadata: Mapping[str, Union[int, float, str, bool]] = field(default_factory=dict)
```

시퀀스형 씬도 유사하게 표현되지만, 대신 Frame의 목록을 포함합니다.

```python
class SceneSeq(BaseModel):
    external_id: str
    frames: List[Frame]
    sensor_specification: SensorSpecification
    calibration_id: Optional[str] # Required if using lidar sensors
    metadata: Mapping[str, Union[int, float, str, bool]] = field(default_factory=dict)
```

### 외부 ID(External Id)

씬은 생성될 때 자동으로 UUID를 부여받습니다. 이 UUID는 Kognic과 모든 내부 시스템에서 기본 식별자로 사용됩니다. 또한 특정 씬에 대한 커뮤니케이션을 더 쉽게 하기 위해 씬을 생성할 때 식별자로서 external id도 필요합니다.

### 센서 명세(Sensor Specification)

센서 명세(sensor specification)에는 씬에서 사용되는 카메라 및/또는 라이다 센서에 대한
정보가 포함됩니다.

추가 필드는 선택 사항이며, 카메라 이미지의 순서와 Kognic 어노테이션 앱에서 표시될 때의 사람이 읽기 좋은 센서 이름(예: "FC" 대신 "Front Camera")을 지정하는 데 사용할 수 있습니다.

예를 들어 자차에 위치한 세 개의 카메라 센서 `R`, `F`, `L`이 있다고 가정해 봅시다. 센서 명세를 생성하는 방법은 다음과 같습니다.

```python
from kognic.io.model import SensorSpecification

sensor_spec = SensorSpecification(
    sensor_to_pretty_name={
        "R": "Right Camera",
        "F": "Front Camera",
        "L": "Left Camera"
    },
    sensor_order=["L", "F", "R"]
)
```

`sensor_order`는 카메라 이미지의 순서를 설정하고, `sensor_to_pretty_name`은 Kognic 어노테이션 앱에서 표시될 때의 레이블에 영향을 줍니다.

### 캘리브레이션

라이다와 카메라 센서로 구성된 씬은 캘리브레이션이 필요합니다. 캘리브레이션은 센서 간의 공간적 관계(위치와 회전) 및 카메라의 내부 파라미터를 지정합니다.

다만 라이다 센서가 없는 씬은 캘리브레이션이 필요하지 않습니다.

Kognic 어노테이션 앱은 캘리브레이션을 사용하여 카메라 이미지가 선택되었을 때 포인트 클라우드 내 영역을 투영하고, 마찬가지로 포인트 클라우드에서 선택된 객체(예: 포인트, 큐보이드)를 이미지에 투영합니다.

캘리브레이션을 생성할 때는 모든 센서가 씬에 존재하는 센서와 일치해야 합니다. 그렇지 않으면 씬이 생성되지 않고 Kognic API에서 검증 오류가 반환됩니다.

API를 통해 캘리브레이션을 생성하는 방법에 대한 자세한 문서는 [캘리브레이션 개요](docId\:wpoiuoy8AgVrpxZX-ORxm)에 있습니다.

### 메타데이터

`metadata` 필드를 통해 씬에 메타데이터를 추가할 수 있습니다. 이는 플랫 키-값 쌍으로 구성되며, 중첩된 데이터 구조는 허용되지 않습니다. 메타데이터는 씬에 대한 추가 정보를 포함하는 데 사용할 수 있습니다. 메타데이터는 어노테이터에게 보이지 않지만, Kognic 어노테이션 도구의 동작을 변경할 수 있는 몇 가지 예약된 키워드가 있습니다. 예약된 키워드는 Python 클라이언트의 `MetaData` 객체에서 확인할 수 있습니다.

### 프레임

Frame 객체는 어노테이션할 바이너리 데이터(.jpg, .png, .las 등)와 그 데이터가 어느 센서에서 나온 것인지를 지정합니다. 전체 구조는 유사하지만 `Frame` 객체는 씬 타입마다 다름에 유의하세요(자세한 내용은 아래 참고).

### 비시퀀스형 프레임

예를 들어 세 개의 카메라 센서 `R`, `F`, `L`의 이미지로 구성된 씬을 만들고 싶다고 가정해 봅시다. 해당 바이너리 데이터는 파일 `img_cam_R.jpg`, `img_cam_F.jpg`, `img_cam_F.jpg`에 있습니다. 이는 씬 타입 `Cameras`에 해당합니다.

```python
from kognic.io.model.scene.resources import Image
from kognic.io.model.scene.cameras import Cameras, Frame

cameras_scene = Cameras(
    ...,
    frame=Frame(
        images=[
            Image("img_cam_R.jpg", sensor_name="R"),
            Image("img_cam_F.jpg", sensor_name="F"),
            Image("img_cam_L.jpg", sensor_name="L"),
        ]
    )
)
```

마찬가지로 센서 `VDL-64`에서 나온 연관된 라이다 포인트 클라우드와 해당 바이너리 파일 `scan_vdl_64.las`가 있었다면, 대신 씬 타입 `LidarsAndCameras`를 사용할 것입니다. `Frame` 클래스는 해당 씬 타입 아래에서 임포트해야 함에 유의하세요.

```python
from kognic.io.model.scene.resources import Image, PointCloud
from kognic.io.model.scene.lidars_and_cameras import LidarsAndCameras, Frame

lidars_and_cameras = LidarsAndCameras(
    ...,
    frame=Frame(
        images=[
            Image("img_cam_R.jpg", sensor_name="R"),
            Image("img_cam_F.jpg", sensor_name="F"),
            Image("img_cam_L.jpg", sensor_name="L"),
        ],
        point_clouds=[
            PointCloud("scan_vdl_64.las", sensor_name="VDL-64")
        ]
    )

)
```

### 시퀀스형 프레임

시퀀스형 씬은 단일 `Frame` 대신 `Frame` 객체의 목록을 받습니다. 또한 시퀀스형 씬과 연관된 `Frame` 객체는 `frame_id`, `relative_timestamp`, `metadata`의 세 가지 추가 파라미터를 가집니다.

시퀀스 관계는 `Frame` 목록의 순서를 통해 표현됩니다.

서로 다른 프레임 사이에 얼마나 시간이 경과했는지 표현하기 위해 각 `Frame`마다 `relative_timestamp` 파라미터를 사용할 수 있습니다. 상대 타임스탬프는 밀리초 단위로 표현되며 `Frame`과 씬 시작 시점 사이의 상대적인 시간을 설명합니다.

예를 들어 센서 데이터가 2Hz로 수집 및 집계된다고 가정해 봅시다.

```python
frame_1 = Frame(..., relative_timestamp=0)
frame_2 = Frame(..., relative_timestamp=500)
frame_3 = Frame(..., relative_timestamp=1000)
frames = [frame_1, frame_2, frame_3]
```

`frame_id`는 목록 내 각 프레임을 고유하게 식별하는 문자열입니다.

일반적인 사용 사례는 각 `frame_id`에 uuid를 사용하거나, `external_id`와 `frame_index`를 조합하는 것입니다. 예를 들어 씬의 `external_id`가 `shanghai_20200101`이라면, `frame_id`는 첫 번째 프레임에 대해 `shanghai_20200101:0`, 두 번째 프레임에 대해 `shanghai_20200101:1` 등으로 인코딩될 수 있습니다.

시퀀스형 프레임에 대해 *프레임* 수준에서 `metadata`를 제공하는 것도 가능합니다. 이는 *플랫* 키-값 쌍으로 구성되며 어노테이션 작업 중 어노테이터에게 노출되지 않습니다.

예를 들어 두 센서 `R`과 `L`의 카메라 이미지를 가진 2개의 프레임으로 구성된 `CamerasSequence` 타입의 씬을 만들고 싶다고 가정해 봅시다.

```python
from kognic.io.model.scene.resources import Image
from kognic.io.model.scene.cameras_sequence import CamerasSequence, Frame

frames = [
    Frame(
        frame_id="1",
        relative_timestamp=0,
        images=[
            Image("img_L_1.jpg", sensor_name='L'),
            Image("img_R_1.jpg", sensor_name='R')
        ]),
    Frame(
        frame_id="2",
        relative_timestamp=500,
        images=[
            Image("img_L_2.jpg", sensor_name='L'),
            Image("img_R_2.jpg", sensor_name='R')
        ])
]

cameras_sequence = CamerasSequence(frames=frames, ...)
```

## 이미지 & 포인트 클라우드 리소스

센서 데이터를 담고 있는 모든 파일은 `Resource`로 표현되며,
`Image`와 `PointCloud`가 그 구체적인 서브클래스입니다.

```python
class Resource(ABC, BaseSerializer):
    filename: str
    resource_id: Optional[str] = None
    sensor_name: str
    file_data: Optional[FileData] = Field(default=None, exclude=True)
```

`Resource`는 궁극적으로 바이너리 또는 텍스트 형태의 센서 데이터를 얻는 방법을 설명하며, 이는 다음과 같은 다양한 방식으로 이루어질 수 있습니다.

1. 간접적으로: 데이터를 담고 있는 로컬 파일명을 참조하여
2. 직접적으로: 생성 시점에 바이트와 유사한 객체를 제공하여
3. 지연 방식으로: 나중에 바이트를 제공할 수 있는 콜백 함수를 제공하여

`Resource`에는 항상 `filename`이 주어져야 합니다. 방법 1의 경우 이는 업로드할 로컬 파일을 가리켜야 합니다. 방법 2와 3의 경우 `filename` 파라미터의 값은 식별자로 취급됩니다. 이는 업로드된 파일의 이름을 지정하는 데 사용되지만 사용자의 파일시스템과 일치할 필요는 없습니다.

`Resource`는 항상 해당 리소스를 캡처한 센서를 식별하는 `sensor_name`을 가집니다. 시퀀스형 씬에서는 각 `Frame`이 각 센서마다 하나의 `Resource`를 가집니다.

위에 나열된 방법 2와 3의 경우, 데이터의 소스를 지정하기 위해 `FileData` 객체가 `Resource`(`Image` 또는 `PointCloud`)에 첨부됩니다. `FileData`는 `data: UploadableData` 또는 `callback: Callable[[str], UploadableData]` 중 하나와, 바이트에 담긴 데이터의 타입을 식별하는 `format`으로 생성됩니다. 이에 대한 예제는 아래에 나와 있습니다. `UploadableData`는 지원되는 원시 데이터 소스에 대한 타입 별칭입니다: `bytes`, `BinaryIO`, `IOBase`, 그리고 `bytes`의 제너레이터와 비동기 제너레이터입니다.

이전 API 클라이언트 릴리스에서는 `gs://bucket/path/file`과 같은 외부 URI로부터 파일을 수집하는 기능을 지원한다고 안내했습니다. 이 기능이 앞으로도 필요하다고 생각되면 Kognic에 문의하세요.

### 로컬 파일

`filename`을 로컬 파일의 경로로 설정하고 다른 수단(직접 또는 콜백)으로 데이터를 제공하지 않습니다. 콘텐츠는 파일명의 접미사로부터 추론된 콘텐츠 타입을 사용하여 업로드됩니다.

```python
Image(filename="/path/to/images/img_FC.png", sensor_name="FC")
```

### 메모리 내 데이터

`filename`에 더해 `file_data` 속성을 통해 `FileData` 객체를 제공하며, 이 객체의 `data` 속성은 `UploadableData`입니다. 이 예제는 원시 `bytes`를 사용합니다.

```python
Image(
    filename="FC-frame15",
    sensor_name="FC",
    file_data=FileData(data=b'some PNG bytes', format=FileData.Format.PNG)
)
```

### 콜백을 통한 데이터

`filename`에 더해 `file_data` 속성을 통해 `FileData` 객체를 제공하며, `UploadableData`를 생성하는 `callback` 함수를 함께 제공합니다. 예를 들면 다음과 같습니다.

```python
Image(
    filename="FC-frame15",
    sensor_name="FC",
    file_data=FileData(callback=get_png, format=FileData.Format.PNG)
)
```

콜백 함수(`get_png`)는 다음과 같은 시그니처를 가진 단항 함수입니다.

```python
def get_png(filename: str) -> UploadableData:
    pass
```

콜백 함수는 해당 단일 파일을 업로드할 시점에 `Resource.filename`을 인자로 하여 호출됩니다.

콜백에 추가 인자가 필요한 경우, 다음과 같이 추가 인자에 대한 클로저를 생성하는 것을 권장합니다.

```python
def get_callback(arg1, arg2, **kwargs):
     def callback(filename) -> bytes:
         # ... use arg1, arg2, filename and kwargs

     return callback

FileData(
    callback=get_callback("foo", "bar", extra1="baz", extra2="qux"),
    format=FileData.Format.JPG
)
```

### 비동기 콜백을 통한 데이터

비동기 콜백은 특히 데이터를 로컬에서 사용할 수 없는 경우 데이터 업로드 속도를 높이는 데 유용합니다. 동기 콜백과 마찬가지로, 해당 단일 파일을 업로드할 시점에 `Resource.filename`을 인자로 하여 콜백 함수가 호출됩니다. 비동기 콜백은 다음과 같이 사용할 수 있습니다.

```python
async def get_png(filename: str) -> UploadableData:
    pass

Image(
    filename="FC-frame15",
    sensor_name="FC",
    file_data=FileData(callback=get_png, format=FileData.Format.PNG)
)
```

### 데이터 스트림

`filename`에 더해 `file_data` 속성을 통해 `FileData` 객체를 제공하며, 이 객체는 `callback` 속성으로 `bytes` 제너레이터 또는 비동기 제너레이터를 가집니다. 이 예제는 비동기 제너레이터를 사용하여 로컬 파일을 작은 청크로 매우 느리게 스트리밍합니다.

```python
async def slow_stream(filename: str) -> AsyncGenerator[bytes, Any]:
    with open(filename, "rb") as f:
        while chunk := f.read(1024):
            asyncio.sleep(1)
            yield chunk

Image(
    filename="FC-frame15",
    sensor_name="FC",
    file_data=FileData(callback=slow_stream, format=FileData.Format.PNG)
)
```

## IMU 데이터

관성 측정 장치(IMU) 데이터는 라이다 포인트 클라우드를 포함하는 씬에 대해 제공될 수 있습니다. 이는 다중 라이다 구성에서 모션 보상을 수행하는 데 사용될 수 있으며, 기본적으로 IMU 데이터가 제공되면 이 작업이 수행됩니다. 모션 보상은 업로드 전에 이미 수행된 경우와 같은 상황을 위해 [씬 기능 플래그](docId\:ZczicgMDIRwPIy6xIaslN)를 통해 비활성화할 수 있습니다.

[모션 보상](docId\:SDgyFJM5eF6B9NV-1PfZG)을 참고하세요.

## 씬 기능 플래그

씬 생성의 선택적인 부분에 대한 제어는 씬 생성 작업을 호출할 때 전달되는 `FeatureFlags`를 통해 가능합니다. 자세한 내용은 [씬 기능 플래그](docId\:ZczicgMDIRwPIy6xIaslN)를 참고하세요.
