概要
さまざまな種類のシーン
シーンとは、まとめてアノテーションされるべきセンサーデータ(カメラ画像、ライダー点群)のグループを表します。シーンには、センサー間の関係、カメラ解像度、センサー名、データ記録頻度に関する情報が含まれます。 シーンはセンサータイプによって異なります。カメラのみのデータにはCamerasシーンタイプを使用します。ライダーとカメラを組み合わせたデータにはLidarsAndCamerasタイプを使用します。シーンは単一フレームまたはシーケンスタイプのいずれかです。
シーケンシャルシーンと非シーケンシャルシーン
シーケンシャルシーンはフレームの時系列シーケンスを表し、非シーケンシャルシーンは1つのセンサーデータのスナップショットのみを含みます。シーケンシャルの関係はフレームのシーケンスで表現され、各フレームにはセンサーデータ情報と相対タイムスタンプが含まれます。非シーケンシャルシーンにはタイムスタンプ情報は不要です。
シーケンシャルシーンタイプは、タイプ名の末尾に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)シーケンシャルシーンも同様の構造で表現されますが、単一のフレームの代わりにフレームのリストを含みます。
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
シーンは作成時に自動的にUUIDが付与され、Kognicの主要な識別子として機能します。シーンをより簡単に参照するために、外部IDも必要です。
センサー仕様
使用するカメラおよび/またはライダーセンサーに関する情報を含みます。オプションフィールドでは、カメラ画像の順序や、アノテーションアプリに表示される人が読みやすいセンサー名(例:「FC」の代わりに「Front Camera」)を指定できます。
例として、エゴビークルに3つのカメラセンサーR、F、Lが搭載されているとします。センサー仕様の作成は以下のようになります。
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アノテーションアプリによって、カメラ画像が選択された際に点群内の領域を投影するため、また同様に点群内で選択されたオブジェクト(点、キュービoidなど)を画像に投影するために使用されます。キャリブレーションを作成する際、すべてのセンサーはシーンに存在するセンサーと一致している必要があります。一致しない場合、シーンは作成されず、Kognic APIからバリデーションエラーが返されます。APIを介したキャリブレーションの作成に関する詳細なドキュメントは、キャリブレーション概要に記載されています
メタデータ
メタデータはmetadataフィールドを介してシーンに追加できます。フラットなキーと値のペアで構成されており、ネストされたデータ構造は使用できません。メタデータはシーンに関する追加情報を含めるために使用できます。メタデータはアノテーターには表示されませんが、Kognicアノテーションツールの動作を変更できる予約済みキーワードがいくつかあります。予約済みキーワードはPythonクライアントのmetadataオブジェクトに記載されています。
フレー
Frameオブジェクトは、アノテーション対象のバイナリデータ(.jpg、.png、.lasなど)と、そのデータの元となったセンサーを指定します。Frameオブジェクトは全体的な構造は似ていますが、シーンタイプごとに異なることに注意してください(詳細は以下を参照)。
非シーケンシャルフレー
例として、3つのカメラセンサーR、F、Lからの画像で構成されるシーンを作成するとします。対応するバイナリデータはファイルimg_cam_R.jpg、img_cam_F.jpg、img_cam_L.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の3つの追加パラメータがあります。シーケンシャルの関係は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とフレームインデックスを組み合わせる方法があります。例えば、シーンのexternal_idがshanghai_20200101の場合、frame_idは最初のフレームをshanghai_20200101:0、2番目のフレームをshanghai_20200101:1というようにエンコードできます。シーケンシャルフレームではフレームレベルのメタデータを提供することも可能です。フラットなキーと値のペアで構成され、アノテーション作業中にアノテーターには表示されません。例として、2つのセンサー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をローカルファイルのパスに設定し、他の方法(直接またはコールバック)でデータを提供しないでください。コンテンツはfilenameのサフィックスから推定されたコンテンツタイプを使用してアップロードされます
Image(filename="/path/to/images/img_FC.png", sensor_name="FC")メモリ内データ
filenameに加えて、file_data属性を介してFileDataオブジェクトを提供します。FileDataオブジェクトはUploadableDataをdata属性として持ちます。この例では生のbytesを使用します。
Image(
filename="FC-frame15",
sensor_name="FC",
file_data=FileData(data=b'some PNG bytes', format=FileData.Format.PNG)
)コールバックからのデータ
filenameに加えて、file_data属性を介してFileDataオブジェクトを提供します。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オブジェクトを提供します。FileDataオブジェクトのcallback属性にはバイトジェネレーターまたは非同期ジェネレーターを指定します。この例では、非同期ジェネレーターを使用してローカルファイルを小さなチャンクで非常にゆっくりとストリーミングします。
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で制御できます。詳細はフィーチャーフラグのドキュメントを参照してください。