lhofstetter commited on
Commit
ca4f53f
·
verified ·
1 Parent(s): c18491c

Upload data_types.py

Browse files
Files changed (1) hide show
  1. data_types.py +571 -0
data_types.py ADDED
@@ -0,0 +1,571 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from bitstring import BitString
3
+ from datetime import datetime
4
+ import math
5
+ import numpy as np
6
+ from dataclasses import dataclass, field, asdict, astuple, InitVar, fields, is_dataclass, _is_classvar, _is_dataclass_instance
7
+ from typing import List, Dict, Tuple
8
+ import cv2 as cv
9
+ import json
10
+ import base64
11
+
12
+
13
+ @dataclass
14
+ class RailNumber:
15
+ hex_data: str = field(init=True, default=False)
16
+ company_ref: int = field(init=False, default=False)
17
+ direction: int = field(init=False, default=False)
18
+ vehicle_number: str = field(init=False, default=False)
19
+ vehicle_group: int = field(init=False, default=False)
20
+ country_id: int = field(init=False, default=False)
21
+ serial_number: int = field(init=False, default=False)
22
+ fleet_id: int = field(init=False, default=False)
23
+ fleet_member_id: int = field(init=False, default=False)
24
+ check_digit: int = field(init=False, default=False)
25
+ decoding_flag: int = field(init=False, default=False) # indicating what kind of decoding error occured, no err = 0
26
+ digits_vehicle_number: str = field(init=False, default=False)
27
+ is_valid: bool = field(init=False, default=False)
28
+
29
+ def __post_init__(self):
30
+ if self.hex_data:
31
+ self.decoding_flag = self.decode_hex()
32
+ if self.vehicle_number:
33
+ self.digits_vehicle_number = self.vehicle_number.replace(" ", "")
34
+ self.digits_vehicle_number = self.digits_vehicle_number.replace("-", "")
35
+ if len(self.digits_vehicle_number) >= 12:
36
+ self.vehicle_group = int(self.digits_vehicle_number[0:2])
37
+ self.country_id = int(self.digits_vehicle_number[2:4])
38
+ self.serial_number = int(self.digits_vehicle_number[4:11])
39
+ self.fleet_id = int(self.digits_vehicle_number[5:8])
40
+ self.fleet_member_id = int(self.digits_vehicle_number[8:11])
41
+ self.check_digit = int(self.digits_vehicle_number[11:])
42
+ self.is_valid = self.check_if_valid()
43
+
44
+ def decode_hex(self) -> int:
45
+ epc_binary_string = BitString(hex=self.hex_data).bin
46
+ epc_header = epc_binary_string[0:8] # epc header has length of 8 bits and for GIAI-96 should be 00110100
47
+ if not epc_header == '00110100':
48
+ return 1
49
+ epc_filter = epc_binary_string[8:11] # filter value has length 3, and can only be 001 which stands for rail
50
+ if not epc_filter == '001':
51
+ return 2
52
+ partition_value = int(epc_binary_string[11:14], base=2) # indicates len of comp. prefix and asset ref
53
+ # should check that 0<= partition value <= 6
54
+ if not 0 <= partition_value <= 6:
55
+ return 3
56
+ company_prefix_bits = 40 - (
57
+ 3 * partition_value + math.floor(partition_value / 3)) # resolve parition value to bit length
58
+ company_digits = 12 - partition_value # resolve partition value to num of company prefix digits
59
+ # get num of bits according to partition value, then convert binary to int and get string of that int
60
+ epc_company_ref = str(int(epc_binary_string[14:14 + company_prefix_bits], base=2))
61
+ # now lets check that decoded company prefix digits have length that they are supposed to have
62
+ if not len(epc_company_ref) == company_digits:
63
+ return 4
64
+ self.company_ref = int(epc_company_ref)
65
+ # GIAI-96 has 96 bits, minus 14 bits for header, filter and partition leaves 82 bits for company prefix and asset ref
66
+ asset_reference_bits = 82 - company_prefix_bits
67
+ asset_reference_digits_max = 25 - company_digits # max. asset reference digits, max does not have to be reached!!
68
+ epc_asset_ref_binary = epc_binary_string[14 + company_prefix_bits:] # get binary part of epc string
69
+ if not len(epc_asset_ref_binary) == asset_reference_bits:
70
+ return 5
71
+ epc_asset_ref = str(int(epc_asset_ref_binary, base=2)) # whole asset ref num string
72
+ # now lets check that decoded company prefix digits have length that they are supposed to have
73
+ if not len(epc_asset_ref) <= asset_reference_digits_max:
74
+ return 6
75
+ ## now follows the splitting of asset reference according to OTIF Einheitliche Technische Vorschriften
76
+ direction_flag = epc_asset_ref[0:1]
77
+ self.direction = int(direction_flag)
78
+ rail_asset_number = epc_asset_ref[1:] # get whole asset number to calculate and cross check the check digit
79
+ self.vehicle_number = " ".join([rail_asset_number[0:2], rail_asset_number[2:4],
80
+ rail_asset_number[4:11], rail_asset_number[11:]])
81
+ if not 1 <= self.direction <= 2:
82
+ return 7
83
+ elif not len(rail_asset_number) == 12:
84
+ return 8
85
+ else:
86
+ return 0
87
+
88
+ def check_if_valid(self) -> bool:
89
+ check_sum = 0
90
+ for index, digit in enumerate(self.digits_vehicle_number):
91
+ multiplier = int(((-1) ** index + 3) * 0.5) # take digits with odd place with weight 2, even placed with 1
92
+ weighted_value = multiplier * int(digit)
93
+ check_sum = check_sum + (weighted_value % 10) + int(math.floor(weighted_value / 10))
94
+ expected_check_digit = int(10 * math.ceil(check_sum / 10)) - check_sum
95
+ return expected_check_digit == self.check_digit
96
+
97
+
98
+ @dataclass
99
+ class RfidRaw:
100
+ sys_time_stamp: float = field(init=True, default=False)
101
+ read_data: bytes = field(init=True, default=False)
102
+
103
+
104
+ @dataclass
105
+ class RfidData:
106
+ sys_time_stamp: float = field(init=True, default=False)
107
+ reader_time_stamp: int = field(init=True, default=False)
108
+ rssi: int = field(init=True, default=False)
109
+ frequency: int = field(init=True, default=False)
110
+ tag_phase: int = field(init=True, default=False)
111
+ hex_data: str = field(init=True, default=False)
112
+
113
+
114
+ @dataclass
115
+ class RfidEvalHex:
116
+ sys_time_stamp: float = field(init=True, default=False)
117
+ sys_time_span_read: float = field(init=True, default=False)
118
+ reader_time_stamp: float = field(init=True, default=False)
119
+ num_reads: int = field(init=True, default=False)
120
+ rssi_min: float = field(init=True, default=False)
121
+ rssi_mean: float = field(init=True, default=False)
122
+ rssi_max: float = field(init=True, default=False)
123
+ hex_data: str = field(init=True, default=False)
124
+
125
+
126
+ @dataclass
127
+ class RfidEval:
128
+ year: int = field(init=True, default=False)
129
+ month: int = field(init=True, default=False)
130
+ day: int = field(init=True, default=False)
131
+ hour: int = field(init=True, default=False)
132
+ minute: int = field(init=True, default=False)
133
+ second: int = field(init=True, default=False)
134
+ millisecond: int = field(init=True, default=False)
135
+
136
+ sys_time_stamp: float = field(init=True, default=False)
137
+ sys_time_span_read: float = field(init=True, default=False)
138
+ reader_time_stamp: float = field(init=True, default=False)
139
+ num_reads: int = field(init=True, default=False)
140
+ rssi_min: float = field(init=True, default=False)
141
+ rssi_mean: float = field(init=True, default=False)
142
+ rssi_max: float = field(init=True, default=False)
143
+
144
+ company_ref: int = field(init=True, default=False)
145
+ direction: int = field(init=True, default=False)
146
+ vehicle_number: str = field(init=True, default=False)
147
+ vehicle_group: int = field(init=True, default=False)
148
+ country_id: int = field(init=True, default=False)
149
+ fleet_id: int = field(init=True, default=False)
150
+
151
+ rfid_eval_hex: InitVar[RfidEvalHex] = field(init=True, default=False)
152
+ rail_number: InitVar[RailNumber] = field(init=True, default=False)
153
+
154
+ def __post_init__(self, rfid_eval_hex: RfidEvalHex, rail_number: RailNumber):
155
+ if rfid_eval_hex:
156
+ self.sys_time_stamp = rfid_eval_hex.sys_time_stamp
157
+ self.sys_time_span_read = rfid_eval_hex.sys_time_span_read
158
+ self.reader_time_stamp = rfid_eval_hex.reader_time_stamp
159
+ self.num_reads = rfid_eval_hex.num_reads
160
+ self.rssi_min = rfid_eval_hex.rssi_min
161
+ self.rssi_mean = rfid_eval_hex.rssi_mean
162
+ self.rssi_max = rfid_eval_hex.rssi_max
163
+ if rail_number:
164
+ self.company_ref = rail_number.company_ref
165
+ self.direction = rail_number.direction
166
+ self.vehicle_number = rail_number.vehicle_number
167
+ self.vehicle_group = rail_number.vehicle_group
168
+ self.country_id = rail_number.country_id
169
+ self.fleet_id = rail_number.fleet_id
170
+ if self.sys_time_stamp:
171
+ dt_object = datetime.fromtimestamp(self.sys_time_stamp)
172
+ self.year = dt_object.year
173
+ self.month = dt_object.month
174
+ self.day = dt_object.day
175
+ self.hour = dt_object.hour
176
+ self.minute = dt_object.minute
177
+ self.second = dt_object.second
178
+ self.millisecond = int(dt_object.microsecond / 1000)
179
+
180
+
181
+ @dataclass
182
+ class RfidEvalFault:
183
+ year: int = field(init=False, default=False)
184
+ month: int = field(init=False, default=False)
185
+ day: int = field(init=False, default=False)
186
+ hour: int = field(init=False, default=False)
187
+ minute: int = field(init=False, default=False)
188
+ second: int = field(init=False, default=False)
189
+ millisecond: int = field(init=False, default=False)
190
+
191
+ sys_time_stamp: float = field(init=False, default=False)
192
+ sys_time_span_read: float = field(init=False, default=False)
193
+ reader_time_stamp: float = field(init=False, default=False)
194
+ num_reads: int = field(init=False, default=False)
195
+ rssi_min: float = field(init=False, default=False)
196
+ rssi_mean: float = field(init=False, default=False)
197
+ rssi_max: float = field(init=False, default=False)
198
+ hex_data: str = field(init=False, default=False)
199
+ decoding_flag: int = field(init=False, default=False)
200
+ company_ref: int = field(init=False, default=False)
201
+ direction: int = field(init=False, default=False)
202
+ vehicle_number: str = field(init=False, default=False)
203
+ vehicle_number_is_valid: bool = field(init=False, default=False)
204
+
205
+ rfid_eval_hex: InitVar[RfidEvalHex] = field(init=True, default=False)
206
+ rail_number: InitVar[RailNumber] = field(init=True, default=False)
207
+
208
+ def __post_init__(self, rfid_eval_hex: RfidEvalHex, rail_number: RailNumber):
209
+ if rfid_eval_hex:
210
+ self.sys_time_stamp = rfid_eval_hex.sys_time_stamp
211
+ self.sys_time_span_read = rfid_eval_hex.sys_time_span_read
212
+ self.reader_time_stamp = rfid_eval_hex.reader_time_stamp
213
+ self.num_reads = rfid_eval_hex.num_reads
214
+ self.rssi_min = rfid_eval_hex.rssi_min
215
+ self.rssi_mean = rfid_eval_hex.rssi_mean
216
+ self.rssi_max = rfid_eval_hex.rssi_max
217
+ self.hex_data = rfid_eval_hex.hex_data
218
+ if rail_number:
219
+ self.company_ref = rail_number.company_ref
220
+ self.direction = rail_number.direction
221
+ self.vehicle_number = rail_number.vehicle_number
222
+ self.decoding_flag = rail_number.decoding_flag
223
+ self.vehicle_number_is_valid = rail_number.is_valid
224
+ if self.sys_time_stamp:
225
+ dt_object = datetime.fromtimestamp(self.sys_time_stamp)
226
+ self.year = dt_object.year
227
+ self.month = dt_object.month
228
+ self.day = dt_object.day
229
+ self.hour = dt_object.hour
230
+ self.minute = dt_object.minute
231
+ self.second = dt_object.second
232
+ self.millisecond = int(dt_object.microsecond / 1000)
233
+
234
+
235
+ @dataclass
236
+ class CamSourceData:
237
+ time_stamp: float = field(init=True, default=False)
238
+ gain: float = field(init=True, default=False)
239
+ exposure_time: float = field(init=True, default=False)
240
+ conversion_gain: str = field(init=True, default=False)
241
+ gamma: float = field(init=True, default=False)
242
+ img_path_name: str = field(init=True, default=False)
243
+ json_path_name: str = field(init=True, default=False)
244
+
245
+
246
+ @dataclass
247
+ class ImageMatchedData:
248
+ year: int = field(init=True, default=False)
249
+ month: int = field(init=True, default=False)
250
+ day: int = field(init=True, default=False)
251
+ hour: int = field(init=True, default=False)
252
+ minute: int = field(init=True, default=False)
253
+ second: int = field(init=True, default=False)
254
+ millisecond: int = field(init=True, default=False)
255
+ time_stamp_image: float = field(init=True, default=False)
256
+
257
+ time_stamp_rfid: float = field(init=True, default=False)
258
+ company_ref: int = field(init=True, default=False)
259
+ vehicle_number: str = field(init=True, default=False)
260
+ vehicle_group: int = field(init=True, default=False)
261
+ country_id: int = field(init=True, default=False)
262
+ fleet_id: int = field(init=True, default=False)
263
+ direction: int = field(init=True, default=False)
264
+ img_name: str = field(init=True, default=False)
265
+
266
+ cam_source_data: InitVar[CamSourceData] = field(init=True, default=False)
267
+ rfid_eval: InitVar[RfidEval] = field(init=True, default=False)
268
+
269
+ def __post_init__(self, cam_source_data: CamSourceData, rfid_eval: RfidEval):
270
+ if cam_source_data:
271
+ self.time_stamp_image = cam_source_data.time_stamp
272
+ dt_object = datetime.fromtimestamp(self.time_stamp_image)
273
+ self.year = dt_object.year
274
+ self.month = dt_object.month
275
+ self.day = dt_object.day
276
+ self.hour = dt_object.hour
277
+ self.minute = dt_object.minute
278
+ self.second = dt_object.second
279
+ self.millisecond = int(dt_object.microsecond / 1000)
280
+ self.img_name = cam_source_data.img_path_name
281
+
282
+ if rfid_eval:
283
+ self.time_stamp_rfid = rfid_eval.sys_time_stamp
284
+ self.company_ref = rfid_eval.company_ref
285
+ self.vehicle_number = rfid_eval.vehicle_number
286
+ self.vehicle_group = rfid_eval.vehicle_group
287
+ self.country_id = rfid_eval.country_id
288
+ self.fleet_id = rfid_eval.fleet_id
289
+ self.direction = rfid_eval.direction
290
+
291
+
292
+ @dataclass
293
+ class BBoxCoordinates:
294
+ x_min: int = field(init=True, default=False)
295
+ x_max: int = field(init=True, default=False)
296
+ y_min: int = field(init=True, default=False)
297
+ y_max: int = field(init=True, default=False)
298
+ x_center: float = field(init=False, default=False)
299
+ y_center: float = field(init=False, default=False)
300
+ height: int = field(init=False, default=False)
301
+ width: int = field(init=False, default=False)
302
+ as_array: np.ndarray = field(init=False, default=False)
303
+
304
+ def __post_init__(self):
305
+ self.height = self.x_max - self.x_min
306
+ self.width = self.y_max - self.y_min
307
+ self.x_center = 0.5 * (self.x_max + self.x_min)
308
+ self.y_center = 0.5 * (self.y_max + self.y_min)
309
+
310
+ def shift_coordinates(self, x_shift, y_shift, x_new_low_bound=None, x_new_upp_bound=None,
311
+ y_new_low_bound=None, y_new_upp_bound=None):
312
+ self.x_min = self.x_min - x_shift
313
+ if x_new_low_bound is not None:
314
+ self.x_min = max(self.x_min, x_new_low_bound)
315
+ self.x_max = self.x_max - x_shift
316
+ if x_new_upp_bound is not None:
317
+ self.x_max = min(self.x_max, x_new_upp_bound)
318
+ self.y_min = self.y_min - y_shift
319
+ if y_new_low_bound is not None:
320
+ self.y_min = max(self.y_min, y_new_low_bound)
321
+ self.y_max = self.y_max - y_shift
322
+ if y_new_upp_bound is not None:
323
+ self.y_max = min(self.y_max, y_new_upp_bound)
324
+ self.height = self.x_max - self.x_min
325
+ self.width = self.y_max - self.y_min
326
+ self.x_center = 0.5 * (self.x_max + self.x_min)
327
+ self.y_center = 0.5 * (self.y_max + self.y_min)
328
+
329
+ def check_valid(self):
330
+ if self.width and self.height:
331
+ return True
332
+ else:
333
+ return False
334
+
335
+ def make_yolo_label_string(self, label_number, img_size, float_precision):
336
+ final_string = str(int(label_number))
337
+ for this_float in [self.y_center, self.x_center, self.width, self.height]:
338
+ final_string = final_string + ' ' + '{:.{n}f}'.format(this_float/img_size, n=float_precision)
339
+ return final_string
340
+
341
+
342
+ @dataclass
343
+ class StripMeasuredData:
344
+ year: int = field(init=True, default=False)
345
+ month: int = field(init=True, default=False)
346
+ day: int = field(init=True, default=False)
347
+ hour: int = field(init=True, default=False)
348
+ minute: int = field(init=True, default=False)
349
+ second: int = field(init=True, default=False)
350
+ millisecond: int = field(init=True, default=False)
351
+ time_stamp_image: float = field(init=True, default=False)
352
+ img_name: str = field(init=True, default=False)
353
+
354
+ time_stamp_rfid: float = field(init=True, default=False)
355
+ company_ref: int = field(init=True, default=False)
356
+ vehicle_number: str = field(init=True, default=False)
357
+ vehicle_group: int = field(init=True, default=False)
358
+ country_id: int = field(init=True, default=False)
359
+ fleet_id: int = field(init=True, default=False)
360
+ direction: int = field(init=True, default=False)
361
+
362
+ bounding_box_a: BBoxCoordinates = field(init=True, default=False)
363
+ bounding_box_b: BBoxCoordinates = field(init=True, default=False)
364
+ estimated_euler_angles: np.ndarray = field(init=True, default=np.zeros((3, 1)))
365
+ estimated_distances: np.ndarray = field(init=True, default=np.zeros((3, 1)))
366
+ profile_a: np.ndarray = field(init=True, default=np.zeros((601, 2)))
367
+ profile_b: np.ndarray = field(init=True, default=np.zeros((601, 2)))
368
+ sliding_strip_type: str = field(init=True, default=False)
369
+
370
+ img_matched_source_data: InitVar[ImageMatchedData] = field(init=True, default=False)
371
+
372
+ def __post_init__(self, img_matched_source_data: ImageMatchedData):
373
+ if img_matched_source_data:
374
+ self.year = img_matched_source_data.year
375
+ self.month = img_matched_source_data.month
376
+ self.day = img_matched_source_data.day
377
+ self.hour = img_matched_source_data.hour
378
+ self.minute = img_matched_source_data.minute
379
+ self.second = img_matched_source_data.second
380
+ self.millisecond = img_matched_source_data.millisecond
381
+ self.img_name = os.path.basename(img_matched_source_data.img_name)
382
+ self.time_stamp_rfid = img_matched_source_data.time_stamp_rfid
383
+ self.company_ref = img_matched_source_data.company_ref
384
+ self.vehicle_number = img_matched_source_data.vehicle_number
385
+ self.vehicle_group = img_matched_source_data.vehicle_group
386
+ self.country_id = img_matched_source_data.country_id
387
+ self.fleet_id = img_matched_source_data.fleet_id
388
+ self.direction = img_matched_source_data.direction
389
+
390
+
391
+ @dataclass
392
+ class ImageMeasuredData:
393
+ year: int = field(init=True, default=False)
394
+ month: int = field(init=True, default=False)
395
+ day: int = field(init=True, default=False)
396
+ hour: int = field(init=True, default=False)
397
+ minute: int = field(init=True, default=False)
398
+ second: int = field(init=True, default=False)
399
+ millisecond: int = field(init=True, default=False)
400
+ time_stamp_image: float = field(init=True, default=False)
401
+
402
+ time_stamp_rfid: float = field(init=True, default=False)
403
+ company_ref: int = field(init=True, default=False)
404
+ vehicle_number: str = field(init=True, default=False)
405
+ vehicle_group: int = field(init=True, default=False)
406
+ country_id: int = field(init=True, default=False)
407
+ fleet_id: int = field(init=True, default=False)
408
+ direction: int = field(init=True, default=False)
409
+ img_name: str = field(init=True, default=False)
410
+ measurement_obj_path: str = field(init=True, default=False)
411
+ measurement_txt_path: str = field(init=True, default=False)
412
+
413
+ @classmethod
414
+ def from_image_matched_data(cls, img_matched_data: ImageMatchedData, measurement_obj_path: str, measurement_txt_path: str):
415
+ new_dict = asdict(img_matched_data)
416
+ new_dict['measurement_obj_path'] = measurement_obj_path
417
+ new_dict['measurement_txt_path'] = measurement_txt_path
418
+ return cls(**new_dict)
419
+
420
+
421
+ @dataclass
422
+ class ImgSyncedData:
423
+ img_name: str = field(init=True, default=False)
424
+ img_name_remote: str = field(init=True, default=False)
425
+ measurement_txt_path: str = field(init=True, default=False)
426
+ measurement_txt_name_remote: str = field(init=True, default=False)
427
+
428
+
429
+ @dataclass
430
+ class ImageSourceData:
431
+ img_file_path: str = field(init=True)
432
+ json_file_path: str = field(init=True)
433
+ img_array: np.ndarray = field(init=False)
434
+ meta_data: dict = field(init=False)
435
+
436
+ def load_source_data(self):
437
+ self.img_array = cv.imread(self.img_file_path, cv.IMREAD_ANYDEPTH)
438
+ with open(self.json_file_path) as this_json_file:
439
+ self.meta_data = json.load(this_json_file)
440
+
441
+ def __post_init__(self):
442
+ file_path_tuple = (self.img_file_path, self.json_file_path)
443
+
444
+
445
+ @dataclass
446
+ class ApiCamSourceData: # data type for source data storage, not public, just image data no matching/rfid
447
+ # Data related to image file, required for I/O operations and logistical stuff
448
+ image_base_name: str # serves as identifier for storage & human readability, contains .file_ending
449
+ image_unique_id: str # hashed property or similar, has to be enforced to be globally unique
450
+ image_file_type: str # redundant & expected to stay .png, however for compatibility reasons this will be included
451
+ image_file_size: int # given in bytes in the stored version on disk, may differ if encoding/compression changes !
452
+ image_file_name_path: str # the absolute path where image is stored
453
+ image_json_file_name_path: str # the absolute path where image meta data is stored as json file or similar
454
+
455
+ image_data_encoding: str # how the byte-string is to be read and decoded, like 'base64', 'uint16' or 'utf-8'
456
+ image_size_bytes: int # specifying the size of the image_data in bytes, important for data transmission
457
+ image_data: str # the encoded image data as string
458
+ image_json_dict: dict # the data in the adjacent json file as dictionary
459
+
460
+ # Data related to the optical result & parameters of the image
461
+ image_width: int # for decoding robustness & data validation, important for image analysis
462
+ image_height: int # for decoding robustness & data validation, important for image analysis
463
+ image_pixel_format: str # expected to stay 'Mono16', could become 'Mono12', 'Mono8' or sth different however
464
+ image_exposure_time: float # given in nanoseconds, might be obsolete
465
+ image_analog_gain: float # given in decibels, might be obsolete
466
+ image_flash_wave_length: float # given in nanometers, might be obsolete
467
+
468
+ # Data related to camera which produced image, needed for image analysis & traceability
469
+ camera_focal_length: float # [mm] contains information on how to convert pixels into distances
470
+ camera_pixel_size_width: float # [mm] contains information on how to convert pixels into distances
471
+ camera_pixel_size_height: float # [mm] contains information on how to convert pixels into distances
472
+ camera_optical_center_width: int # given in pixels referenced to current left border, usually 1/2 of height
473
+ camera_optical_center_height: int # given in pixels referenced to current top border, usually 1/2 of width
474
+
475
+ # Data related to the time & location of the taken image
476
+ time_stamp_utc: float # standard utc time_stamp
477
+ time_zone: str # important in order to convert utc timestamp correctly into date time formats
478
+ location_latitude: float # not relevant now, but will be in the future, inherited from camera reference
479
+ location_longitude: float # not relevant now, but will be in the future, inherited from camera reference
480
+ location_track_ref: int # not relevant now, but will be in the future, inherited from camera reference
481
+ camera_machine_ref: str # unique camera identifier, revealing model, firmware, etc.
482
+
483
+ def convert_to_json_format(self):
484
+ with open(self.image_file_name_path, "rb") as file_buffer:
485
+ self.image_data = base64.b64encode(file_buffer.read()).decode()
486
+ self.image_size_bytes = len(self.image_data)
487
+ self.image_data_encoding = 'base64'
488
+ with open(self.image_json_file_name_path) as json_file:
489
+ self.image_json_dict = json.load(json_file)
490
+
491
+ def save_data_from_json(self):
492
+ img_data_as_bytes = base64.b64decode(self.image_data.encode())
493
+ with open(self.image_file_name_path, 'wb') as image_file:
494
+ image_file.write(img_data_as_bytes)
495
+ with open(self.image_json_file_name_path, "w") as json_file:
496
+ json.dump(self.image_json_dict, json_file)
497
+
498
+
499
+ @dataclass # this is now basically the same class as ApiCamSourceData
500
+ class ApiPrivatePictureData: # data type for source data storage, not public, just image data no matching/rfid
501
+ # Data related to image file, required for I/O operations and logistical stuff
502
+ image_base_name: str # serves as identifier for storage & human readability, contains .file_ending
503
+ image_unique_id: str # hashed property or similar, has to be enforced to be globally unique
504
+ image_file_type: str # redundant & expected to stay .png, however for compatibility reasons this will be included
505
+ image_file_size: int # given in bytes in the stored version on disk, may differ if encoding/compression changes !
506
+ image_file_name_path: str # the absolute path where image is stored
507
+ image_json_file_name_path: str # the absolute path where image meta data is stored as json file or similar
508
+
509
+ # Data related to the optical result & parameters of the image
510
+ image_width: int # for decoding robustness & data validation, important for image analysis
511
+ image_height: int # for decoding robustness & data validation, important for image analysis
512
+ image_pixel_format: str # expected to stay 'Mono16', could become 'Mono12', 'Mono8' or sth different however
513
+ image_exposure_time: float # given in nanoseconds, might be obsolete
514
+ image_analog_gain: float # given in decibels, might be obsolete
515
+ image_flash_wave_length: float # given in nanometers, might be obsolete
516
+
517
+ # Data related to camera which produced image, needed for image analysis & traceability
518
+ camera_focal_length: float # [mm] contains information on how to convert pixels into distances
519
+ camera_pixel_size_width: float # [mm] contains information on how to convert pixels into distances
520
+ camera_pixel_size_height: float # [mm] contains information on how to convert pixels into distances
521
+ camera_optical_center_width: int # given in pixels referenced to current left border, usually 1/2 of height
522
+ camera_optical_center_height: int # given in pixels referenced to current top border, usually 1/2 of width
523
+
524
+ # Data related to the time & location of the taken image
525
+ time_stamp_utc: int # standard utc time_stamp
526
+ time_zone: str # important in order to convert utc timestamp correctly into date time formats
527
+ location_latitude: float # not relevant now, but will be in the future, inherited from camera reference
528
+ location_longitude: float # not relevant now, but will be in the future, inherited from camera reference
529
+ location_track_ref: int # not relevant now, but will be in the future, inherited from camera reference
530
+ camera_machine_ref: str # unique camera identifier, revealing model, firmware, etc.
531
+
532
+
533
+ @dataclass
534
+ class ApiClientPictureData: # data given to clients, attributes inherited from source data
535
+ # Data related to image format, encoding and decoding
536
+ image_base_name: str # serves as unique identifier for all images, contains .file_ending
537
+ image_file_type: str # redundant & expected to stay .png, however for compatibility reasons this will be included
538
+ image_width: int # for decoding robustness & data validation, important for image analysis
539
+ image_height: int # for decoding robustness & data validation, important for image analysis
540
+ image_pixel_format: str # expected to stay 'Mono16', could become 'Mono12', 'Mono8' or sth different however
541
+ image_data_encoding: str # how the byte-string is to be read and decoded, like 'base64', 'uint16' or 'utf-8'
542
+ image_size_bytes: int # specifying the size of the image_data in bytes, important for data transmission
543
+ image_data: str # the encoded image data as string
544
+
545
+ # Data related to the time stamp of the image, has duplicated information for convenience
546
+ time_stamp_utc: int # standard utc time_stamp
547
+ time_zone: str # important in order to convert utc timestamp correctly into date time formats
548
+ year: int # redundant for convenience
549
+ month: int # redundant for convenience
550
+ day: int # redundant for convenience
551
+ hour: int # redundant for convenience
552
+ minute: int # redundant for convenience
553
+ second: int # redundant for convenience
554
+
555
+ # RFID-Tag related data, maybe include additional rfid-tags read in a close time window revealing train composition
556
+ has_matched_rfid_tag: bool # for database reasons and clarifying that the sent rfid data below is valid or not
557
+ company_ref: int # from epc-tag, may not always indicate the actual owner of the vehicle, important for filtering
558
+ vehicle_number: str # 12 digit european rail asset number, carrying all important vehicle data
559
+ vehicle_group: int # redundant for convenience
560
+ country_id: int # redundant for convenience
561
+ vehicle_number_national: int # redundant for convenience
562
+ fleet_id: int # redundant for convenience
563
+ direction_flag: int # can be used to get a better understanding of the pantograph orientation and location
564
+
565
+ # Data related to the location of the picture taken, possibly direction of movement of the train
566
+ image_location_latitude: float # not relevant now, but will be in the future, inherited from camera reference
567
+ image_location_longitude: float # not relevant now, but will be in the future, inherited from camera reference
568
+ image_location_track_ref: int # not relevant now, but will be in the future, inherited from camera reference
569
+ image_train_direction_of_movement: float # train heading in degrees, or +1/-1 if the given track defines direction
570
+
571
+