Examples

These examples may help you get started with PyHeadTracker. You can find more here.

Supperware

This example demonstrates how to use the Supperware Head Tracker with PyHeadTracker and send the orientation data via OSC to the IEM SceneRotator.

import pyheadtracker as pht

osc_send = pht.out.IEMSceneRotator(ip="127.0.0.1", port=7000)

ht = pht.supperware.HeadTracker1(
    device_name="Head Tracker 1",
    device_name_output="Head Tracker 2",
    refresh_rate=50,
    compass_on=True,
    orient_format="q",
)

ht.open(compass_force_calibration=False)
ht.zero()

while True:
    try:
        orientation = ht.read_orientation()

        if isinstance(orientation, pht.Quaternion):
            osc_send.send_orientation(orientation)
            # Print the quaternion values for debugging
            print(
                f"WXYZ: {orientation[0]:7.2f} {orientation[1]:7.2f} {orientation[2]:7.2f} {orientation[3]:7.2f}",
                end="\r",
            )

    except (EOFError, KeyboardInterrupt):
        print("\nClosing connection.")
        ht.close()
        break

Camera-based tracking

This example demonstrates how to use the MediaPipe Face Landmarker for webcam-based head tracking with PyHeadTracker. The orientation data is sent via OSC to the IEM SceneRotator.

import pyheadtracker as pht

scenerotator_send = pht.out.IEMSceneRotator(ip="127.0.0.1", port=7000)
roomencoder_send = pht.out.IEMRoomEncoder(ip="127.0.0.1", port=7001, mode="listener")

ht = pht.cam.MPFaceLandmarker(0, orient_format="ypr")

ht.open()
ht.zero()

while True:
    try:
        pose = ht.read_pose()

        if pose is not None:
            orientation = pose["orientation"]
            position = pose["position"]
        else:
            continue

        if isinstance(orientation, pht.YPR):
            scenerotator_send.send_orientation(orientation)

        if isinstance(position, pht.Position):
            roomencoder_send.send_position(position)

        if isinstance(orientation, pht.YPR) and isinstance(position, pht.Position):
            print(
                f"Yaw: {orientation.yaw:.2f}, Pitch: {orientation.pitch:.2f}, Roll: {orientation.roll:.2f} | X: {position.x:.2f}, Y: {position.y:.2f}, Z: {position.z:.2f}",
                end="\r",
            )

    except (EOFError, KeyboardInterrupt):
        print("\nClosing connection.")
        ht.close()
        break

openXR

In this example, we use the openXR bindings to retrieve head tracking data from a head mounted display, while rendering a dummy OpenGL scene. Orientation data is sent via OSC to the IEM SceneRotator and position data to the IEM RoomEncoder.

import pyheadtracker as pht
from OpenGL import GL
import xr
from xr.utils.gl import ContextObject
from xr.utils.gl.glfw_util import GLFWOffscreenContextProvider

scenerotator_send = pht.out.IEMSceneRotator(ip="127.0.0.1", port=7000)
roomencoder_send = pht.out.IEMRoomEncoder(ip="127.0.0.1", port=7001, mode="listener")

with ContextObject(
    instance_create_info=xr.InstanceCreateInfo(
        enabled_extension_names=[
            # A graphics extension is mandatory (without a headless extension)
            xr.KHR_OPENGL_ENABLE_EXTENSION_NAME,
        ],
    ),
    context_provider=GLFWOffscreenContextProvider(),
) as context:
    # Create a head tracker using OpenXR
    ht = pht.hmd.openXR(context)

    eye_colors = [
        (0, 1, 0, 1),  # Left eye green
        (0, 0, 1, 1),  # Right eye blue
        (1, 0, 0, 1),  # Third eye blind
    ]

    run_idx = 0

    try:
        for frame_index, frame_state in enumerate(context.frame_loop()):
            for view_index, view in enumerate(context.view_loop(frame_state)):
                run_idx += 1

                if run_idx == 20:
                    ht.zero()
                GL.glClearColor(*eye_colors[view_index])
                GL.glClear(GL.GL_COLOR_BUFFER_BIT)

                orientation = ht.read_orientation(frame_state)
                position = ht.read_position(frame_state)

                if orientation is not None:

                    scenerotator_send.send_orientation(orientation)

                    orientation_ypr = pht.utils.quat2ypr(orientation).to_degrees()
                    print(
                        f"Yaw: {orientation_ypr[0]:7.2f} {orientation_ypr[1]:7.2f} {orientation_ypr[2]:7.2f}",
                        end="\r",
                    )

                if position is not None:
                    roomencoder_send.send_position(position)

                    # print(
                    #     f"XYZ: {position.x:7.2f} {position.y:7.2f} {position.z:7.2f}",
                    #     end="\r",
                    # )

    except (EOFError, KeyboardInterrupt):
        print("\nClosing connection.")