Skip to content

Commit 86d353a

Browse files
authored
Merge pull request #48 from domstoppable/main
Adds a function for calibration retrieval
2 parents ce2938b + 5e66be3 commit 86d353a

File tree

5 files changed

+77
-0
lines changed

5 files changed

+77
-0
lines changed

docs/examples/simple.rst

+10
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ Eyes camera video
9191
:emphasize-lines: 18
9292
:linenos:
9393

94+
Camera calibration
95+
------------------
96+
97+
.. note:: Only available when connecting to a Neon Companion app
98+
99+
.. literalinclude:: ../../examples/simple/camera_calibration.py
100+
:language: python
101+
:emphasize-lines: 12
102+
:linenos:
103+
94104

95105
.. _stream_video_with_overlayed_gaze_example_simple:
96106

examples/simple/camera_calibration.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from pupil_labs.realtime_api.simple import discover_one_device
2+
3+
# Look for devices. Returns as soon as it has found the first device.
4+
print("Looking for the next best device...")
5+
device = discover_one_device(max_search_duration_seconds=10)
6+
if device is None:
7+
print("No device found.")
8+
raise SystemExit(-1)
9+
10+
# Device status is fetched on initialization and kept up-to-date in the background
11+
12+
calibration = device.get_calibration()
13+
14+
print("Scene camera matrix:")
15+
print(calibration["scene_camera_matrix"][0])
16+
print("\nScene distortion coefficients:")
17+
print(calibration["scene_distortion_coefficients"][0])
18+
19+
print("\nRight camera matrix:")
20+
print(calibration["right_camera_matrix"][0])
21+
print("\nRight distortion coefficients:")
22+
print(calibration["right_distortion_coefficients"][0])
23+
24+
print("\nLeft camera matrix:")
25+
print(calibration["left_camera_matrix"][0])
26+
print("\nLeft distortion coefficients:")
27+
print(calibration["left_distortion_coefficients"][0])
28+
29+
device.close()

src/pupil_labs/realtime_api/device.py

+30
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import typing as T
77

88
import aiohttp
9+
import numpy as np
910
import websockets
1011

1112
import pupil_labs # noqa: F401
@@ -165,6 +166,35 @@ async def __aexit__(
165166
def _create_client_session(self):
166167
self.session = aiohttp.ClientSession()
167168

169+
async def get_calibration(self) -> np.ndarray:
170+
"""
171+
:raises pupil_labs.realtime_api.device.DeviceError: if the request fails
172+
"""
173+
async with self.session.get(self.api_url(APIPath.CALIBRATION)) as response:
174+
if response.status != 200:
175+
raise DeviceError(response.status, "Failed to fetch calibration")
176+
177+
raw_data = await response.read()
178+
return np.frombuffer(
179+
raw_data,
180+
np.dtype(
181+
[
182+
("version", "u1"),
183+
("serial", "6a"),
184+
("scene_camera_matrix", "(3,3)d"),
185+
("scene_distortion_coefficients", "8d"),
186+
("scene_extrinsics_affine_matrix", "(4,4)d"),
187+
("right_camera_matrix", "(3,3)d"),
188+
("right_distortion_coefficients", "8d"),
189+
("right_extrinsics_affine_matrix", "(4,4)d"),
190+
("left_camera_matrix", "(3,3)d"),
191+
("left_distortion_coefficients", "8d"),
192+
("left_extrinsics_affine_matrix", "(4,4)d"),
193+
("crc", "u4"),
194+
]
195+
),
196+
)
197+
168198

169199
class StatusUpdateNotifier:
170200
def __init__(self, device: Device, callbacks: T.List[UpdateCallback]) -> None:

src/pupil_labs/realtime_api/models.py

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class APIPath(enum.Enum):
1919
RECORDING_STOP_AND_SAVE = "/recording:stop_and_save"
2020
RECORDING_CANCEL = "/recording:cancel"
2121
EVENT = "/event"
22+
CALIBRATION = "/../calibration.bin"
2223

2324
def full_address(
2425
self, address: str, port: int, protocol: str = "http", prefix: str = "/api"

src/pupil_labs/realtime_api/simple/device.py

+7
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ def world_sensor(self) -> T.Optional[Sensor]:
114114
def gaze_sensor(self) -> T.Optional[Sensor]:
115115
return self._status.direct_gaze_sensor()
116116

117+
def get_calibration(self):
118+
async def _get_calibration():
119+
async with _DeviceAsync.convert_from(self) as control:
120+
return await control.get_calibration()
121+
122+
return asyncio.run(_get_calibration())
123+
117124
def recording_start(self) -> str:
118125
"""Wraps :py:meth:`pupil_labs.realtime_api.device.Device.recording_start`
119126

0 commit comments

Comments
 (0)