photonlibpy 2025.0.0b1__py3-none-any.whl → 2025.0.0b2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. photonlibpy/__init__.py +2 -2
  2. photonlibpy/estimation/__init__.py +5 -0
  3. photonlibpy/estimation/cameraTargetRelation.py +25 -0
  4. photonlibpy/estimation/openCVHelp.py +200 -0
  5. photonlibpy/estimation/rotTrlTransform3d.py +32 -0
  6. photonlibpy/estimation/targetModel.py +137 -0
  7. photonlibpy/estimation/visionEstimation.py +91 -0
  8. photonlibpy/generated/MultiTargetPNPResultSerde.py +1 -1
  9. photonlibpy/generated/PhotonPipelineMetadataSerde.py +1 -1
  10. photonlibpy/generated/PhotonPipelineResultSerde.py +1 -1
  11. photonlibpy/generated/PhotonTrackedTargetSerde.py +1 -1
  12. photonlibpy/generated/PnpResultSerde.py +1 -1
  13. photonlibpy/generated/TargetCornerSerde.py +1 -1
  14. photonlibpy/generated/__init__.py +0 -1
  15. photonlibpy/networktables/NTTopicSet.py +64 -0
  16. photonlibpy/networktables/__init__.py +1 -0
  17. photonlibpy/packet.py +17 -9
  18. photonlibpy/photonCamera.py +9 -6
  19. photonlibpy/photonPoseEstimator.py +3 -3
  20. photonlibpy/simulation/__init__.py +5 -0
  21. photonlibpy/simulation/photonCameraSim.py +408 -0
  22. photonlibpy/simulation/simCameraProperties.py +661 -0
  23. photonlibpy/simulation/videoSimUtil.py +2 -0
  24. photonlibpy/simulation/visionSystemSim.py +237 -0
  25. photonlibpy/simulation/visionTargetSim.py +50 -0
  26. photonlibpy/targeting/TargetCorner.py +5 -1
  27. photonlibpy/targeting/__init__.py +1 -1
  28. photonlibpy/targeting/multiTargetPNPResult.py +8 -2
  29. photonlibpy/targeting/photonPipelineResult.py +7 -4
  30. photonlibpy/targeting/photonTrackedTarget.py +7 -1
  31. photonlibpy/version.py +2 -2
  32. {photonlibpy-2025.0.0b1.dist-info → photonlibpy-2025.0.0b2.dist-info}/METADATA +6 -2
  33. photonlibpy-2025.0.0b2.dist-info/RECORD +36 -0
  34. photonlibpy-2025.0.0b1.dist-info/RECORD +0 -22
  35. {photonlibpy-2025.0.0b1.dist-info → photonlibpy-2025.0.0b2.dist-info}/WHEEL +0 -0
  36. {photonlibpy-2025.0.0b1.dist-info → photonlibpy-2025.0.0b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,661 @@
1
+ import logging
2
+ import math
3
+ import typing
4
+
5
+ import cv2 as cv
6
+ import numpy as np
7
+ from wpimath.geometry import Rotation2d, Rotation3d, Translation3d
8
+ from wpimath.units import hertz, seconds
9
+
10
+ from ..estimation import RotTrlTransform3d
11
+
12
+
13
+ class SimCameraProperties:
14
+ def __init__(self, path: str | None = None, width: int = 0, height: int = 0):
15
+ self.resWidth: int = -1
16
+ self.resHeight: int = -1
17
+ self.camIntrinsics: np.ndarray = np.zeros((3, 3)) # [3,3]
18
+ self.distCoeffs: np.ndarray = np.zeros((8, 1)) # [8,1]
19
+ self.avgErrorPx: float = 0.0
20
+ self.errorStdDevPx: float = 0.0
21
+ self.frameSpeed: seconds = 0.0
22
+ self.exposureTime: seconds = 0.0
23
+ self.avgLatency: seconds = 0.0
24
+ self.latencyStdDev: seconds = 0.0
25
+ self.viewplanes: list[np.ndarray] = [] # [3,1]
26
+
27
+ if path is None:
28
+ self.setCalibration(960, 720, fovDiag=Rotation2d(math.radians(90.0)))
29
+ else:
30
+ raise Exception("not yet implemented")
31
+
32
+ def setCalibration(
33
+ self,
34
+ width: int,
35
+ height: int,
36
+ *,
37
+ fovDiag: Rotation2d | None = None,
38
+ newCamIntrinsics: np.ndarray | None = None,
39
+ newDistCoeffs: np.ndarray | None = None,
40
+ ):
41
+ # Should be an inverted XOR on the args to differentiate between the signatures
42
+
43
+ has_fov_args = fovDiag is not None
44
+ has_matrix_args = newCamIntrinsics is not None and newDistCoeffs is not None
45
+
46
+ if (has_fov_args and has_matrix_args) or (
47
+ not has_matrix_args and not has_fov_args
48
+ ):
49
+ raise Exception("not a correct function sig")
50
+
51
+ if has_fov_args:
52
+ if fovDiag.degrees() < 1.0 or fovDiag.degrees() > 179.0:
53
+ fovDiag = Rotation2d.fromDegrees(
54
+ max(min(fovDiag.degrees(), 179.0), 1.0)
55
+ )
56
+ logging.error(
57
+ "Requested invalid FOV! Clamping between (1, 179) degrees..."
58
+ )
59
+
60
+ resDiag = math.sqrt(width * width + height * height)
61
+ diagRatio = math.tan(fovDiag.radians() / 2.0)
62
+ fovWidth = Rotation2d(math.atan((diagRatio * (width / resDiag)) * 2))
63
+ fovHeight = Rotation2d(math.atan(diagRatio * (height / resDiag)) * 2)
64
+
65
+ newDistCoeffs = np.zeros((8, 1))
66
+
67
+ cx = width / 2.0 - 0.5
68
+ cy = height / 2.0 - 0.5
69
+
70
+ fx = cx / math.tan(fovWidth.radians() / 2.0)
71
+ fy = cy / math.tan(fovHeight.radians() / 2.0)
72
+
73
+ newCamIntrinsics = np.array([[fx, 0.0, cx], [0.0, fy, cy], [0.0, 0.0, 1.0]])
74
+
75
+ # really convince python we are doing the right thing
76
+ assert newCamIntrinsics is not None
77
+ assert newDistCoeffs is not None
78
+
79
+ self.resWidth = width
80
+ self.resHeight = height
81
+ self.camIntrinsics = newCamIntrinsics
82
+ self.distCoeffs = newDistCoeffs
83
+
84
+ p = [
85
+ Translation3d(
86
+ 1.0,
87
+ Rotation3d(
88
+ 0.0,
89
+ 0.0,
90
+ (self.getPixelYaw(0) + Rotation2d(math.pi / 2.0)).radians(),
91
+ ),
92
+ ),
93
+ Translation3d(
94
+ 1.0,
95
+ Rotation3d(
96
+ 0.0,
97
+ 0.0,
98
+ (self.getPixelYaw(width) + Rotation2d(math.pi / 2.0)).radians(),
99
+ ),
100
+ ),
101
+ Translation3d(
102
+ 1.0,
103
+ Rotation3d(
104
+ 0.0,
105
+ 0.0,
106
+ (self.getPixelPitch(0) + Rotation2d(math.pi / 2.0)).radians(),
107
+ ),
108
+ ),
109
+ Translation3d(
110
+ 1.0,
111
+ Rotation3d(
112
+ 0.0,
113
+ 0.0,
114
+ (self.getPixelPitch(height) + Rotation2d(math.pi / 2.0)).radians(),
115
+ ),
116
+ ),
117
+ ]
118
+
119
+ self.viewplanes = []
120
+
121
+ for i in p:
122
+ self.viewplanes.append(np.array([i.X(), i.Y(), i.Z()]))
123
+
124
+ def setCalibError(self, newAvgErrorPx: float, newErrorStdDevPx: float):
125
+ self.avgErrorPx = newAvgErrorPx
126
+ self.errorStdDevPx = newErrorStdDevPx
127
+
128
+ def setFPS(self, fps: hertz):
129
+ self.frameSpeed = max(1.0 / fps, self.exposureTime)
130
+
131
+ def setExposureTime(self, newExposureTime: seconds):
132
+ self.exposureTime = newExposureTime
133
+ self.frameSpeed = max(self.frameSpeed, self.exposureTime)
134
+
135
+ def setAvgLatency(self, newAvgLatency: seconds):
136
+ self.vgLatency = newAvgLatency
137
+
138
+ def setLatencyStdDev(self, newLatencyStdDev: seconds):
139
+ self.latencyStdDev = newLatencyStdDev
140
+
141
+ def getResWidth(self) -> int:
142
+ return self.resWidth
143
+
144
+ def getResHeight(self) -> int:
145
+ return self.resHeight
146
+
147
+ def getResArea(self) -> int:
148
+ return self.resWidth * self.resHeight
149
+
150
+ def getAspectRatio(self) -> float:
151
+ return 1.0 * self.resWidth / self.resHeight
152
+
153
+ def getIntrinsics(self) -> np.ndarray:
154
+ return self.camIntrinsics
155
+
156
+ def getDistCoeffs(self) -> np.ndarray:
157
+ return self.distCoeffs
158
+
159
+ def getFPS(self) -> hertz:
160
+ return 1.0 / self.frameSpeed
161
+
162
+ def getFrameSpeed(self) -> seconds:
163
+ return self.frameSpeed
164
+
165
+ def getExposureTime(self) -> seconds:
166
+ return self.exposureTime
167
+
168
+ def getAverageLatency(self) -> seconds:
169
+ return self.avgLatency
170
+
171
+ def getLatencyStdDev(self) -> seconds:
172
+ return self.latencyStdDev
173
+
174
+ def getContourAreaPercent(self, points: list[typing.Tuple[float, float]]) -> float:
175
+ return (
176
+ cv.contourArea(cv.convexHull(np.array(points))) / self.getResArea() * 100.0
177
+ )
178
+
179
+ def getPixelYaw(self, pixelX: float) -> Rotation2d:
180
+ fx = self.camIntrinsics[0, 0]
181
+ cx = self.camIntrinsics[0, 2]
182
+ xOffset = cx - pixelX
183
+ return Rotation2d(fx, xOffset)
184
+
185
+ def getPixelPitch(self, pixelY: float) -> Rotation2d:
186
+ fy = self.camIntrinsics[1, 1]
187
+ cy = self.camIntrinsics[1, 2]
188
+ yOffset = cy - pixelY
189
+ return Rotation2d(fy, -yOffset)
190
+
191
+ def getPixelRot(self, point: typing.Tuple[int, int]) -> Rotation3d:
192
+ return Rotation3d(
193
+ 0.0,
194
+ self.getPixelPitch(point[1]).radians(),
195
+ self.getPixelYaw(point[0]).radians(),
196
+ )
197
+
198
+ def getCorrectedPixelRot(self, point: typing.Tuple[float, float]) -> Rotation3d:
199
+ fx = self.camIntrinsics[0, 0]
200
+ cx = self.camIntrinsics[0, 2]
201
+ xOffset = cx - point[0]
202
+
203
+ fy = self.camIntrinsics[1, 1]
204
+ cy = self.camIntrinsics[1, 2]
205
+ yOffset = cy - point[1]
206
+
207
+ yaw = Rotation2d(fx, xOffset)
208
+ pitch = Rotation2d(fy / math.cos(math.atan(xOffset / fx)), -yOffset)
209
+ return Rotation3d(0.0, pitch.radians(), yaw.radians())
210
+
211
+ def getHorizFOV(self) -> Rotation2d:
212
+ left = self.getPixelYaw(0)
213
+ right = self.getPixelYaw(self.resWidth)
214
+ return left - right
215
+
216
+ def getVertFOV(self) -> Rotation2d:
217
+ above = self.getPixelPitch(0)
218
+ below = self.getPixelPitch(self.resHeight)
219
+ return below - above
220
+
221
+ def getDiagFOV(self) -> Rotation2d:
222
+ return Rotation2d(
223
+ math.hypot(self.getHorizFOV().radians(), self.getVertFOV().radians())
224
+ )
225
+
226
+ def getVisibleLine(
227
+ self, camRt: RotTrlTransform3d, a: Translation3d, b: Translation3d
228
+ ) -> typing.Tuple[float | None, float | None]:
229
+ relA = camRt.apply(a)
230
+ relB = camRt.apply(b)
231
+
232
+ if relA.X() <= 0.0 and relB.X() <= 0.0:
233
+ return (None, None)
234
+
235
+ av = np.array([relA.X(), relA.Y(), relA.Z()])
236
+ bv = np.array([relB.X(), relB.Y(), relB.Z()])
237
+ abv = bv - av
238
+
239
+ aVisible = True
240
+ bVisible = True
241
+
242
+ for normal in self.viewplanes:
243
+ aVisibility = av.dot(normal)
244
+ if aVisibility < 0:
245
+ aVisible = False
246
+
247
+ bVisibility = bv.dot(normal)
248
+ if bVisibility < 0:
249
+ bVisible = False
250
+ if aVisibility <= 0 and bVisibility <= 0:
251
+ return (None, None)
252
+
253
+ if aVisible and bVisible:
254
+ return (0.0, 1.0)
255
+
256
+ intersections = [float("nan"), float("nan"), float("nan"), float("nan")]
257
+
258
+ # Optionally 3x1 vector
259
+ ipts: typing.List[np.ndarray | None] = [None, None, None, None]
260
+
261
+ for i, normal in enumerate(self.viewplanes):
262
+ a_projn = (av.dot(normal) / normal.dot(normal)) * normal
263
+
264
+ if abs(abv.dot(normal)) < 1.0e-5:
265
+ continue
266
+ intersections[i] = a_projn.dot(a_projn) / -(abv.dot(a_projn))
267
+
268
+ apv = intersections[i] * abv
269
+ intersectpt = av + apv
270
+ ipts[i] = intersectpt
271
+
272
+ for j in range(1, len(self.viewplanes)):
273
+ if j == 0:
274
+ continue
275
+ oi = (i + j) % len(self.viewplanes)
276
+ onormal = self.viewplanes[oi]
277
+ if intersectpt.dot(onormal) < 0:
278
+ intersections[i] = float("nan")
279
+ ipts[i] = None
280
+ break
281
+
282
+ if not ipts[i]:
283
+ continue
284
+
285
+ for j in range(i - 1, 0 - 1):
286
+ oipt = ipts[j]
287
+ if not oipt:
288
+ continue
289
+
290
+ diff = oipt - intersectpt
291
+ if abs(diff).max() < 1e-4:
292
+ intersections[i] = float("nan")
293
+ ipts[i] = None
294
+ break
295
+
296
+ inter1 = float("nan")
297
+ inter2 = float("nan")
298
+ for inter in intersections:
299
+ if not math.isnan(inter):
300
+ if math.isnan(inter1):
301
+ inter1 = inter
302
+ else:
303
+ inter2 = inter
304
+
305
+ if not math.isnan(inter2):
306
+ max_ = max(inter1, inter2)
307
+ min_ = min(inter1, inter2)
308
+ if aVisible:
309
+ min_ = 0
310
+ if bVisible:
311
+ max_ = 1
312
+ return (min_, max_)
313
+ elif not math.isnan(inter1):
314
+ if aVisible:
315
+ return (0, inter1)
316
+ if bVisible:
317
+ return (inter1, 1)
318
+ return (inter1, None)
319
+ else:
320
+ return (None, None)
321
+
322
+ def estPixelNoise(self, points: np.ndarray) -> np.ndarray:
323
+ assert points.shape[1] == 1, points.shape
324
+ assert points.shape[2] == 2, points.shape
325
+ if self.avgErrorPx == 0 and self.errorStdDevPx == 0:
326
+ return points
327
+
328
+ noisyPts: list[list] = []
329
+ for p in points:
330
+ error = np.random.normal(self.avgErrorPx, self.errorStdDevPx, 1)[0]
331
+ errorAngle = np.random.uniform(-math.pi, math.pi)
332
+ noisyPts.append(
333
+ [
334
+ [
335
+ float(p[0, 0] + error * math.cos(errorAngle)),
336
+ float(p[0, 1] + error * math.sin(errorAngle)),
337
+ ]
338
+ ]
339
+ )
340
+ retval = np.array(noisyPts, dtype=np.float32)
341
+ assert points.shape == retval.shape, retval
342
+ return retval
343
+
344
+ def estLatency(self) -> seconds:
345
+ return max(
346
+ float(np.random.normal(self.avgLatency, self.latencyStdDev, 1)[0]),
347
+ 0.0,
348
+ )
349
+
350
+ def estSecUntilNextFrame(self) -> seconds:
351
+ return self.frameSpeed + max(0.0, self.estLatency() - self.frameSpeed)
352
+
353
+ @classmethod
354
+ def PERFECT_90DEG(cls) -> typing.Self:
355
+ return cls()
356
+
357
+ @classmethod
358
+ def PI4_LIFECAM_320_240(cls) -> typing.Self:
359
+ prop = cls()
360
+ prop.setCalibration(
361
+ 320,
362
+ 240,
363
+ newCamIntrinsics=np.array(
364
+ [
365
+ [328.2733242048587, 0.0, 164.8190261141906],
366
+ [0.0, 318.0609794305216, 123.8633838438093],
367
+ [0.0, 0.0, 1.0],
368
+ ]
369
+ ),
370
+ newDistCoeffs=np.array(
371
+ [
372
+ [
373
+ 0.09957946553445934,
374
+ -0.9166265114485799,
375
+ 0.0019519890627236526,
376
+ -0.0036071725380870333,
377
+ 1.5627234622420942,
378
+ 0,
379
+ 0,
380
+ 0,
381
+ ]
382
+ ]
383
+ ),
384
+ )
385
+ prop.setCalibError(0.21, 0.0124)
386
+ prop.setFPS(30.0)
387
+ prop.setAvgLatency(30.0e-3)
388
+ prop.setLatencyStdDev(10.0e-3)
389
+ return prop
390
+
391
+ @classmethod
392
+ def PI4_LIFECAM_640_480(cls) -> typing.Self:
393
+ prop = cls()
394
+ prop.setCalibration(
395
+ 640,
396
+ 480,
397
+ newCamIntrinsics=np.array(
398
+ [
399
+ [669.1428078983059, 0.0, 322.53377249329213],
400
+ [0.0, 646.9843137061716, 241.26567383784163],
401
+ [0.0, 0.0, 1.0],
402
+ ]
403
+ ),
404
+ newDistCoeffs=np.array(
405
+ [
406
+ [
407
+ 0.12788470750464645,
408
+ -1.2350335805796528,
409
+ 0.0024990767286192732,
410
+ -0.0026958287600230705,
411
+ 2.2951386729115537,
412
+ 0,
413
+ 0,
414
+ 0,
415
+ ]
416
+ ]
417
+ ),
418
+ )
419
+ prop.setCalibError(0.26, 0.046)
420
+ prop.setFPS(15.0)
421
+ prop.setAvgLatency(65.0e-3)
422
+ prop.setLatencyStdDev(15.0e-3)
423
+ return prop
424
+
425
+ @classmethod
426
+ def LL2_640_480(cls) -> typing.Self:
427
+ prop = cls()
428
+ prop.setCalibration(
429
+ 640,
430
+ 480,
431
+ newCamIntrinsics=np.array(
432
+ [
433
+ [511.22843367007755, 0.0, 323.62049380211096],
434
+ [0.0, 514.5452336723849, 261.8827920543568],
435
+ [0.0, 0.0, 1.0],
436
+ ]
437
+ ),
438
+ newDistCoeffs=np.array(
439
+ [
440
+ [
441
+ 0.1917469998873756,
442
+ -0.5142936883324216,
443
+ 0.012461562046896614,
444
+ 0.0014084973492408186,
445
+ 0.35160648971214437,
446
+ 0,
447
+ 0,
448
+ 0,
449
+ ]
450
+ ]
451
+ ),
452
+ )
453
+ prop.setCalibError(0.25, 0.05)
454
+ prop.setFPS(15.0)
455
+ prop.setAvgLatency(35.0e-3)
456
+ prop.setLatencyStdDev(8.0e-3)
457
+ return prop
458
+
459
+ @classmethod
460
+ def LL2_960_720(cls) -> typing.Self:
461
+ prop = cls()
462
+ prop.setCalibration(
463
+ 960,
464
+ 720,
465
+ newCamIntrinsics=np.array(
466
+ [
467
+ [769.6873145148892, 0.0, 486.1096609458122],
468
+ [0.0, 773.8164483705323, 384.66071662358354],
469
+ [0.0, 0.0, 1.0],
470
+ ]
471
+ ),
472
+ newDistCoeffs=np.array(
473
+ [
474
+ [
475
+ 0.189462064814501,
476
+ -0.49903003669627627,
477
+ 0.007468423590519429,
478
+ 0.002496885298683693,
479
+ 0.3443122090208624,
480
+ 0,
481
+ 0,
482
+ 0,
483
+ ]
484
+ ]
485
+ ),
486
+ )
487
+ prop.setCalibError(0.35, 0.10)
488
+ prop.setFPS(10.0)
489
+ prop.setAvgLatency(50.0e-3)
490
+ prop.setLatencyStdDev(15.0e-3)
491
+ return prop
492
+
493
+ @classmethod
494
+ def LL2_1280_720(cls) -> typing.Self:
495
+ prop = cls()
496
+ prop.setCalibration(
497
+ 1280,
498
+ 720,
499
+ newCamIntrinsics=np.array(
500
+ [
501
+ [1011.3749416937393, 0.0, 645.4955139388737],
502
+ [0.0, 1008.5391755084075, 508.32877656020196],
503
+ [0.0, 0.0, 1.0],
504
+ ]
505
+ ),
506
+ newDistCoeffs=np.array(
507
+ [
508
+ [
509
+ 0.13730101577061535,
510
+ -0.2904345656989261,
511
+ 8.32475714507539e-4,
512
+ -3.694397782014239e-4,
513
+ 0.09487962227027584,
514
+ 0,
515
+ 0,
516
+ 0,
517
+ ]
518
+ ]
519
+ ),
520
+ )
521
+ prop.setCalibError(0.37, 0.06)
522
+ prop.setFPS(7.0)
523
+ prop.setAvgLatency(60.0e-3)
524
+ prop.setLatencyStdDev(20.0e-3)
525
+ return prop
526
+
527
+ @classmethod
528
+ def OV9281_640_480(cls) -> typing.Self:
529
+ prop = cls()
530
+ prop.setCalibration(
531
+ 640,
532
+ 480,
533
+ newCamIntrinsics=np.array(
534
+ [
535
+ [627.1573807284262, 0, 307.79423851611824],
536
+ [0, 626.6621595938243, 219.02625533911998],
537
+ [0, 0, 1],
538
+ ]
539
+ ),
540
+ newDistCoeffs=np.array(
541
+ [
542
+ [
543
+ 0.054834081023049625,
544
+ -0.15994111706817074,
545
+ -0.0017587106009926158,
546
+ -0.0014671022483263552,
547
+ 0.049742166267499596,
548
+ 0,
549
+ 0,
550
+ 0,
551
+ ],
552
+ ]
553
+ ),
554
+ )
555
+ prop.setCalibError(0.25, 0.05)
556
+ prop.setFPS(30.0)
557
+ prop.setAvgLatency(60.0e-3)
558
+ prop.setLatencyStdDev(20.0e-3)
559
+ return prop
560
+
561
+ @classmethod
562
+ def OV9281_800_600(cls) -> typing.Self:
563
+ prop = cls()
564
+ prop.setCalibration(
565
+ 800,
566
+ 600,
567
+ newCamIntrinsics=np.array(
568
+ [
569
+ [783.9467259105329, 0, 384.7427981451478],
570
+ [0, 783.3276994922804, 273.7828191739],
571
+ [0, 0, 1],
572
+ ]
573
+ ),
574
+ newDistCoeffs=np.array(
575
+ [
576
+ [
577
+ 0.054834081023049625,
578
+ -0.15994111706817074,
579
+ -0.0017587106009926158,
580
+ -0.0014671022483263552,
581
+ 0.049742166267499596,
582
+ 0,
583
+ 0,
584
+ 0,
585
+ ],
586
+ ]
587
+ ),
588
+ )
589
+ prop.setCalibError(0.25, 0.05)
590
+ prop.setFPS(25.0)
591
+ prop.setAvgLatency(60.0e-3)
592
+ prop.setLatencyStdDev(20.0e-3)
593
+ return prop
594
+
595
+ @classmethod
596
+ def OV9281_1280_720(cls) -> typing.Self:
597
+ prop = cls()
598
+ prop.setCalibration(
599
+ 1280,
600
+ 720,
601
+ newCamIntrinsics=np.array(
602
+ [
603
+ [940.7360710926395, 0, 615.5884770322365],
604
+ [0, 939.9932393907364, 328.53938300868],
605
+ [0, 0, 1],
606
+ ]
607
+ ),
608
+ newDistCoeffs=np.array(
609
+ [
610
+ [
611
+ 0.054834081023049625,
612
+ -0.15994111706817074,
613
+ -0.0017587106009926158,
614
+ -0.0014671022483263552,
615
+ 0.049742166267499596,
616
+ 0,
617
+ 0,
618
+ 0,
619
+ ],
620
+ ]
621
+ ),
622
+ )
623
+ prop.setCalibError(0.25, 0.05)
624
+ prop.setFPS(15.0)
625
+ prop.setAvgLatency(60.0e-3)
626
+ prop.setLatencyStdDev(20.0e-3)
627
+ return prop
628
+
629
+ @classmethod
630
+ def OV9281_1920_1080(cls) -> typing.Self:
631
+ prop = cls()
632
+ prop.setCalibration(
633
+ 1920,
634
+ 1080,
635
+ newCamIntrinsics=np.array(
636
+ [
637
+ [1411.1041066389591, 0, 923.3827155483548],
638
+ [0, 1409.9898590861046, 492.80907451301994],
639
+ [0, 0, 1],
640
+ ]
641
+ ),
642
+ newDistCoeffs=np.array(
643
+ [
644
+ [
645
+ 0.054834081023049625,
646
+ -0.15994111706817074,
647
+ -0.0017587106009926158,
648
+ -0.0014671022483263552,
649
+ 0.049742166267499596,
650
+ 0,
651
+ 0,
652
+ 0,
653
+ ],
654
+ ]
655
+ ),
656
+ )
657
+ prop.setCalibError(0.25, 0.05)
658
+ prop.setFPS(10.0)
659
+ prop.setAvgLatency(60.0e-3)
660
+ prop.setLatencyStdDev(20.0e-3)
661
+ return prop
@@ -0,0 +1,2 @@
1
+ class VideoSimUtil:
2
+ pass