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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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