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

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