개요
씬의 다양한 타입
씬은 함께 어노테이션되어야 하는 센서 데이터(예: 카메라 이미지, 라이다 포인트 클라우드)의 그룹을 나타냅니다. 센서와 그 캡처된 데이터 사이의 관계를 설명하는 데 필요한 정보, 즉 카메라 해상도, 센서 이름, 데이터가 기록된 빈도 등도 씬에 함께 명시됩니다.
씬의 내용을 표현하는 데 사용되는 센서의 종류에 따라 다양한 씬 타입이 존재합니다. 예를 들어 카메라 센서의 이미지 데이터만으로 씬을 만들고 싶다면 씬 타입 Cameras를 사용합니다. 마찬가지로 라이다와 카메라 센서를 모두 포함하는 씬을 만들고 싶다면 씬 타입 LidarsAndCameras를 사용합니다. 또한 씬은 단일 프레임(single frame) 타입이거나 시퀀스(sequence) 타입일 수 있습니다.
시퀀스형 vs 비시퀀스형
시퀀스형 씬은 시간에 따른 프레임의 시퀀스를 나타내는 반면, 비시퀀스형 씬은 센서 데이터의 스냅샷 하나만을 포함합니다. 시퀀스 관계는 Frame의 시퀀스를 통해 표현되며, 각 Frame은 프레임을 구성하는 센서 데이터의 종류(예: 프레임에 포함된 이미지 및/또는 포인트 클라우드)에 대한 정보와, 다른 프레임을 기준으로 해당 Frame이 시간상 어디에 위치하는지를 나타내는 *상대 타임스탬프(relative timestamp)*를 포함합니다.
비시퀀스형 씬은 단일 Frame만 포함하며 상대 타임스탬프 정보가 필요하지 않습니다.
시퀀스형 씬 타입은 타입 이름에 Seq 접미사가 붙어 식별됩니다.
현재 다음 씬 타입이 지원됩니다.
- Cameras
- LidarsAndCameras
- CamerasSeq
- LidarsAndCamerasSeq
- AggregatedLidarsAndCamerasSeq
씬 필드
비시퀀스형 씬은 다음과 같은 구조를 가집니다.
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의 목록을 포함합니다.
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)External Id
씬은 생성될 때 자동으로 UUID를 부여받습니다. 이 UUID는 Kognic과 모든 내부 시스템에서 기본 식별자로 사용됩니다. 또한 특정 씬에 대한 커뮤니케이션을 더 쉽게 하기 위해 씬을 생성할 때 식별자로서 external id도 필요합니다.
Sensor Specification
sensor specification에는 씬에서 사용되는 카메라 및/또는 라이다 센서에 대한 정보가 포함됩니다.
추가 필드는 선택 사항이며, 카메라 이미지의 순서와 Kognic 어노테이션 앱에서 표시될 때의 사람이 읽기 좋은 센서 이름(예: "FC" 대신 "Front Camera")을 지정하는 데 사용할 수 있습니다.
예를 들어 자차에 위치한 세 개의 카메라 센서 R, F, L이 있다고 가정해 봅시다. sensor specification을 생성하는 방법은 다음과 같습니다.
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 어노테이션 앱에서 표시될 때의 레이블에 영향을 줍니다.
Calibration
라이다와 카메라 센서로 구성된 씬은 캘리브레이션이 필요합니다. 캘리브레이션은 센서 간의 공간적 관계(위치와 회전) 및 카메라의 내부 파라미터를 지정합니다.
다만 라이다 센서가 없는 씬은 캘리브레이션이 필요하지 않습니다.
캘리브레이션은 카메라 이미지가 선택되었을 때 포인트 클라우드 내 영역을 투영하거나, 마찬가지로 선택된 객체(예: 점, 큐보이드)를 포인트 클라우드에서 이미지로 투영하는 데 Kognic 어노테이션 앱에서 사용됩니다.
캘리브레이션을 생성할 때는 모든 센서가 씬에 존재하는 센서와 일치해야 합니다. 그렇지 않으면 씬이 생성되지 않고 Kognic API에서 검증 오류가 반환됩니다.
API를 통해 캘리브레이션을 생성하는 방법에 대한 자세한 문서는 캘리브레이션 개요에 있습니다.
Metadata
metadata 필드를 통해 씬에 메타데이터를 추가할 수 있습니다. 이는 플랫 키-값 쌍으로 구성되며, 중첩된 데이터 구조는 허용되지 않습니다. 메타데이터는 씬에 대한 추가 정보를 포함하는 데 사용할 수 있습니다. 메타데이터는 어노테이터에게 보이지 않지만, Kognic 어노테이션 도구의 동작을 변경할 수 있는 몇 가지 예약된 키워드가 있습니다. 예약된 키워드는 Python 클라이언트의 MetaData 객체에서 확인할 수 있습니다.
Frame
Frame 객체는 어노테이션할 바이너리 데이터(.jpg, .png, .las 등)와 그 데이터가 어느 센서에서 나온 것인지를 지정합니다. 전체 구조는 유사하지만 Frame 객체는 씬 타입마다 다름에 유의하세요(자세한 내용은 아래 참고).
비시퀀스형 프레임
예를 들어 세 개의 카메라 센서 R, F, L의 이미지로 구성된 씬을 만들고 싶다고 가정해 봅시다. 해당 바이너리 데이터는 파일 img_cam_R.jpg, img_cam_F.jpg, img_cam_F.jpg에 있습니다. 이는 씬 타입 Cameras에 해당합니다.
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 클래스는 해당 씬 타입 아래에서 임포트해야 함에 유의하세요.
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로 수집 및 집계된다고 가정해 봅시다.
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 타입의 씬을 만들고 싶다고 가정해 봅시다.
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가 그 구체적인 서브클래스입니다.
class Resource(ABC, BaseSerializer):
filename: str
resource_id: Optional[str] = None
sensor_name: str
file_data: Optional[FileData] = Field(default=None, exclude=True)Resource는 궁극적으로 바이너리 또는 텍스트 형태의 센서 데이터를 얻는 방법을 설명하며, 이는 다음과 같은 다양한 방식으로 이루어질 수 있습니다.
- 간접적으로: 데이터를 담고 있는 로컬 파일명을 참조하여
- 직접적으로: 생성 시점에 바이트와 유사한 객체를 제공하여
- 지연 방식으로: 나중에 바이트를 제공할 수 있는 콜백 함수를 제공하여
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을 로컬 파일의 경로로 설정하고 다른 수단(직접 또는 콜백)으로 데이터를 제공하지 않습니다. 콘텐츠는 파일명의 접미사로부터 추론된 콘텐츠 타입을 사용하여 업로드됩니다.
Image(filename="/path/to/images/img_FC.png", sensor_name="FC")메모리 내 데이터
filename에 더해 file_data 속성을 통해 FileData 객체를 제공하며, 이 객체는 다시 자체적으로 data 속성으로 UploadableData를 가집니다. 이 예제는 원시 bytes를 사용합니다.
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 함수를 함께 제공합니다. 예를 들면 다음과 같습니다.
Image(
filename="FC-frame15",
sensor_name="FC",
file_data=FileData(callback=get_png, format=FileData.Format.PNG)
)콜백 함수(get_png)는 다음과 같은 시그니처를 가진 단항 함수입니다.
def get_png(filename: str) -> UploadableData:
pass콜백 함수는 해당 단일 파일을 업로드할 시점에 Resource.filename을 인자로 하여 호출됩니다.
콜백에 추가 인자가 필요한 경우, 다음과 같이 추가 인자에 대한 클로저를 생성하는 것을 권장합니다.
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을 인자로 하여 콜백 함수가 호출됩니다. 비동기 콜백은 다음과 같이 사용할 수 있습니다.
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 제너레이터 또는 비동기 제너레이터를 가집니다. 이 예제는 비동기 제너레이터를 사용하여 로컬 파일을 작은 청크로 매우 느리게 스트리밍합니다.
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) 데이터는 LIDAR 포인트 클라우드를 포함하는 씬에 대해 제공될 수 있습니다. 이는 다중 라이다 설정에서 모션 보상을 수행하는 데 사용될 수 있으며, 기본적으로 IMU 데이터가 제공되면 이 작업이 수행됩니다. 모션 보상은 업로드 전에 이미 수행된 경우와 같은 상황을 위해 씬 기능 플래그를 통해 비활성화할 수 있습니다.
다중 라이다 설정을 위한 모션 보상을 참고하세요.
씬 기능 플래그
씬 생성의 선택적인 부분에 대한 제어는 씬 생성 작업을 호출할 때 전달되는 FeatureFlags를 통해 가능합니다. 자세한 내용은 기능 플래그 문서를 참고하세요.