2
2
import json
3
3
import os .path
4
4
from dataclasses import dataclass
5
- from typing import Optional , Sequence
6
- from urllib . parse import urlparse
5
+ from typing import Optional , Sequence , Dict , Any
6
+ from enum import Enum
7
7
8
+ from .annotation import is_local_path , Point3D
8
9
from .constants import (
9
10
DATASET_ITEM_ID_KEY ,
10
11
IMAGE_URL_KEY ,
11
12
METADATA_KEY ,
12
13
ORIGINAL_IMAGE_URL_KEY ,
13
14
REFERENCE_ID_KEY ,
15
+ TYPE_KEY ,
16
+ URL_KEY ,
17
+ CAMERA_PARAMS_KEY ,
18
+ POINTCLOUD_URL_KEY ,
19
+ X_KEY ,
20
+ Y_KEY ,
21
+ Z_KEY ,
22
+ W_KEY ,
23
+ POSITION_KEY ,
24
+ HEADING_KEY ,
25
+ FX_KEY ,
26
+ FY_KEY ,
27
+ CX_KEY ,
28
+ CY_KEY ,
14
29
)
15
30
16
31
17
32
@dataclass
18
- class DatasetItem :
33
+ class Quaternion :
34
+ x : float
35
+ y : float
36
+ z : float
37
+ w : float
19
38
20
- image_location : str
39
+ @classmethod
40
+ def from_json (cls , payload : Dict [str , float ]):
41
+ return cls (
42
+ payload [X_KEY ], payload [Y_KEY ], payload [Z_KEY ], payload [W_KEY ]
43
+ )
44
+
45
+ def to_payload (self ) -> dict :
46
+ return {
47
+ X_KEY : self .x ,
48
+ Y_KEY : self .y ,
49
+ Z_KEY : self .z ,
50
+ W_KEY : self .w ,
51
+ }
52
+
53
+
54
+ @dataclass
55
+ class CameraParams :
56
+ position : Point3D
57
+ heading : Quaternion
58
+ fx : float
59
+ fy : float
60
+ cx : float
61
+ cy : float
62
+
63
+ @classmethod
64
+ def from_json (cls , payload : Dict [str , Any ]):
65
+ return cls (
66
+ Point3D .from_json (payload [POSITION_KEY ]),
67
+ Quaternion .from_json (payload [HEADING_KEY ]),
68
+ payload [FX_KEY ],
69
+ payload [FY_KEY ],
70
+ payload [CX_KEY ],
71
+ payload [CY_KEY ],
72
+ )
73
+
74
+ def to_payload (self ) -> dict :
75
+ return {
76
+ POSITION_KEY : self .position .to_payload (),
77
+ HEADING_KEY : self .heading .to_payload (),
78
+ FX_KEY : self .fx ,
79
+ FY_KEY : self .fy ,
80
+ CX_KEY : self .cx ,
81
+ CY_KEY : self .cy ,
82
+ }
83
+
84
+
85
+ class DatasetItemType (Enum ):
86
+ IMAGE = "image"
87
+ POINTCLOUD = "pointcloud"
88
+
89
+
90
+ @dataclass # pylint: disable=R0902
91
+ class DatasetItem : # pylint: disable=R0902
92
+ image_location : Optional [str ] = None
21
93
reference_id : Optional [str ] = None
22
94
item_id : Optional [str ] = None
23
95
metadata : Optional [dict ] = None
96
+ pointcloud_location : Optional [str ] = None
24
97
25
98
def __post_init__ (self ):
26
99
self .local = is_local_path (self .image_location )
100
+ assert bool (self .image_location ) != bool (
101
+ self .pointcloud_location
102
+ ), "Must specify exactly one of the image_location, pointcloud_location parameters"
103
+ self .type = (
104
+ DatasetItemType .IMAGE
105
+ if self .image_location
106
+ else DatasetItemType .POINTCLOUD
107
+ )
108
+ camera_params = (
109
+ self .metadata .get (CAMERA_PARAMS_KEY , None )
110
+ if self .metadata
111
+ else None
112
+ )
113
+ self .camera_params = (
114
+ CameraParams .from_json (camera_params ) if camera_params else None
115
+ )
27
116
28
117
@classmethod
29
- def from_json (cls , payload : dict ):
30
- url = payload .get (IMAGE_URL_KEY , "" ) or payload .get (
118
+ def from_json (cls , payload : dict , is_scene = False ):
119
+ image_url = payload .get (IMAGE_URL_KEY , "" ) or payload .get (
31
120
ORIGINAL_IMAGE_URL_KEY , ""
32
121
)
122
+
123
+ if is_scene :
124
+ return cls (
125
+ image_location = image_url ,
126
+ pointcloud_location = payload .get (POINTCLOUD_URL_KEY , "" ),
127
+ reference_id = payload .get (REFERENCE_ID_KEY , None ),
128
+ item_id = payload .get (DATASET_ITEM_ID_KEY , None ),
129
+ metadata = payload .get (METADATA_KEY , {}),
130
+ )
131
+
33
132
return cls (
34
- image_location = url ,
133
+ image_location = image_url ,
35
134
reference_id = payload .get (REFERENCE_ID_KEY , None ),
36
135
item_id = payload .get (DATASET_ITEM_ID_KEY , None ),
37
136
metadata = payload .get (METADATA_KEY , {}),
@@ -40,28 +139,39 @@ def from_json(cls, payload: dict):
40
139
def local_file_exists (self ):
41
140
return os .path .isfile (self .image_location )
42
141
43
- def to_payload (self ) -> dict :
44
- payload = {
45
- IMAGE_URL_KEY : self .image_location ,
142
+ def to_payload (self , is_scene = False ) -> dict :
143
+ payload : Dict [str , Any ] = {
46
144
METADATA_KEY : self .metadata or {},
47
145
}
146
+
147
+ if is_scene :
148
+ if self .image_location :
149
+ payload [URL_KEY ] = self .image_location
150
+ elif self .pointcloud_location :
151
+ payload [URL_KEY ] = self .pointcloud_location
152
+ payload [TYPE_KEY ] = self .type .value
153
+ else :
154
+ assert (
155
+ self .image_location
156
+ ), "Must specify image_location for DatasetItems not in a Scene"
157
+ payload [IMAGE_URL_KEY ] = self .image_location
158
+
48
159
if self .reference_id :
49
160
payload [REFERENCE_ID_KEY ] = self .reference_id
50
161
if self .item_id :
51
162
payload [DATASET_ITEM_ID_KEY ] = self .item_id
163
+ if self .camera_params :
164
+ payload [CAMERA_PARAMS_KEY ] = self .camera_params
165
+
52
166
return payload
53
167
54
168
def to_json (self ) -> str :
55
169
return json .dumps (self .to_payload (), allow_nan = False )
56
170
57
171
58
- def is_local_path (path : str ) -> bool :
59
- return urlparse (path ).scheme not in {"https" , "http" , "s3" , "gs" }
60
-
61
-
62
172
def check_all_paths_remote (dataset_items : Sequence [DatasetItem ]):
63
173
for item in dataset_items :
64
- if is_local_path (item .image_location ):
174
+ if item . image_location and is_local_path (item .image_location ):
65
175
raise ValueError (
66
176
f"All paths must be remote, but { item .image_location } is either "
67
177
"local, or a remote URL type that is not supported."
0 commit comments