Skip to main content

vigilo-stream

PyPI Documentation Python Rust Maturin License

Zero-copy multi-modal stream fusion engine for real-time AI pipelines in Python.

vigilo-stream provides Python bindings for the stream fusion engine in vigilo-core. It gives Python vision and proctoring pipelines direct access to video frames and temporal rule evaluation without copying memory across the FFI boundary.

  • Zero-copy buffer sharing: Frame memory allocated in Rust is exposed directly to NumPy and PyTorch through __array_interface__ and the buffer protocol.
  • Lock-free frame exchange: Capture workers publish frames through ArcSwap slots, discarding stale frames automatically instead of building queues.
  • Deterministic temporal fusion: The FusionEngine processes detection signals through configurable hysteresis bands, hold timers, and score accumulators. Given the same input, replay produces identical events.
  • Multimodal detection: Wraps the vigilo-core inference pipeline for face detection (YuNet), head pose (MobileNetV3), gaze estimation (MobileGaze), object detection (YOLOX-Nano), and identity matching (ArcFace).

Documentation

Comprehensive usage guides and API references are available in the Documentation:

Installation

pip install vigilo-stream

You can import the library using either vigilo_stream or the rustream alias.

Quick start

import vigilo_stream
import numpy as np

# 1. Zero-copy frame operations (no neural model files required)
frame = vigilo_stream.create_synthetic_frame(1280, 720, seq=1, r=255, g=0, b=0)
print(frame.width, frame.height, frame.shape) # 1280 720 (720, 1280, 3)

# Expose Rust memory directly as a NumPy array without copying
arr = np.asarray(frame)
assert arr.__array_interface__["data"][0] == frame.__array_interface__["data"][0]

# 2. Vision and proctoring pipeline
# Pipeline automatically downloads default model weights on first run
with vigilo_stream.Pipeline(models_dir="models") as pipe:
    pipe.start("camera:0")  # Accepts "camera:0", "file:clip.mp4", or "dir:frames/"

    while pipe.is_running():
        frame = pipe.poll_frame()
        if frame:
            img = np.asarray(frame)

        snapshot = pipe.snapshot()
        if snapshot:
            print(f"Faces: {snapshot.face_count}, Pose: {snapshot.head_pose}")

        events = pipe.events()
        for event in events:
            print(f"Violation: {event}")

# 3. Headless deterministic stream fusion (no neural models or camera required)
engine = vigilo_stream.FusionEngine()
events = engine.replay("recorded_session.jsonl")
print(f"Replayed session produced {len(events)} events.")

Real-time OpenCV visualization with HUD

You can stream frames directly into OpenCV with zero-copy buffer access, overlay bounding boxes with confidence scores, draw 5 facial landmarks, project a 3D head pose orientation gizmo, render gaze direction rays, tag prohibited objects, and display proctoring telemetry cards and violation status pills matching the desktop viewer.

Live OpenCV HUD

Complete example

import cv2
import math
import time
import numpy as np
import vigilo_stream

# Initialize pipeline with automatic model download
pipe = vigilo_stream.Pipeline(models_dir="models", auto_download=True)
pipe.start("camera:0")  # Accepts "camera:0", "file:clip.mp4", or "dir:frames/"

cv2.namedWindow("Vigilo Stream Viewer", cv2.WINDOW_NORMAL)
cv2.resizeWindow("Vigilo Stream Viewer", 1280, 720)

active_violations = set()
held_objects = []
last_object_time = 0.0

try:
    while pipe.is_running():
        # 1. Zero-copy frame access: raw pointer shared directly with NumPy
        frame = pipe.poll_frame()
        if frame is None:
            continue

        rgb = np.asarray(frame)
        img = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)

        # 2. Instantaneous model snapshot and temporal fusion events
        snap = pipe.snapshot()
        for ev in pipe.events():
            if ev.event_type == "ViolationStarted" and ev.violation:
                active_violations.add(ev.violation.kind)
            elif ev.event_type == "ViolationEnded" and ev.violation:
                active_violations.discard(ev.violation.kind)

        # 3. Draw face bounding boxes, 5 landmarks, 3D pose, and gaze rays
        if snap:
            for i, face in enumerate(snap.faces):
                bx = int(face.bbox.x)
                by = int(face.bbox.y)
                bw = int(face.bbox.w)
                bh = int(face.bbox.h)

                # Bounding box with score badge
                cv2.rectangle(img, (bx, by), (bx + bw, by + bh), (94, 197, 34), 2)
                cv2.putText(
                    img,
                    f"FACE {int(face.score * 100)}%",
                    (bx, max(18, by - 8)),
                    cv2.FONT_HERSHEY_SIMPLEX,
                    0.45,
                    (140, 255, 100),
                    1,
                    cv2.LINE_AA,
                )

                # 5 Facial landmarks (right eye, left eye, nose, right mouth, left mouth)
                palette = [
                    (94, 197, 34),
                    (94, 197, 34),
                    (21, 204, 250),
                    (113, 113, 248),
                    (113, 113, 248),
                ]
                for pt, col in zip(face.landmarks, palette):
                    cv2.circle(img, (int(pt[0]), int(pt[1])), 4, col, -1, cv2.LINE_AA)

                # 3D Head pose orientation axes (Euler projection)
                if i == 0 and snap.head_pose:
                    cx, cy = int(bx + bw * 0.5), int(by + bh * 0.5)
                    s = min(bw, bh) * 0.45
                    rad = math.pi / 180.0
                    y_rad = -snap.head_pose.yaw_deg * rad
                    p_rad = snap.head_pose.pitch_deg * rad
                    r_rad = snap.head_pose.roll_deg * rad

                    cyaw, syaw = math.cos(y_rad), math.sin(y_rad)
                    cpit, spit = math.cos(p_rad), math.sin(p_rad)
                    crol, srol = math.cos(r_rad), math.sin(r_rad)

                    x_end = (
                        int(cx + s * (cyaw * crol)),
                        int(cy + s * (cpit * srol + crol * spit * syaw)),
                    )
                    y_end = (
                        int(cx + s * (-cyaw * srol)),
                        int(cy + s * (cpit * crol - spit * syaw * srol)),
                    )
                    z_end = (int(cx + s * syaw), int(cy + s * (-cyaw * spit)))

                    cv2.arrowedLine(img, (cx, cy), x_end, (68, 68, 239), 2, tipLength=0.2)
                    cv2.arrowedLine(img, (cx, cy), y_end, (94, 197, 34), 2, tipLength=0.2)
                    cv2.arrowedLine(img, (cx, cy), z_end, (250, 165, 96), 2, tipLength=0.2)

                # Gaze direction ray originating between the eyes
                if i == 0 and snap.gaze and len(face.landmarks) >= 2:
                    ox = int((face.landmarks[0][0] + face.landmarks[1][0]) * 0.5)
                    oy = int((face.landmarks[0][1] + face.landmarks[1][1]) * 0.5)
                    glen = bw * 1.1
                    gdx = -glen * math.sin(snap.gaze.yaw_rad) * math.cos(snap.gaze.pitch_rad)
                    gdy = -glen * math.sin(snap.gaze.pitch_rad)
                    cv2.arrowedLine(
                        img,
                        (ox, oy),
                        (int(ox + gdx), int(oy + gdy)),
                        (252, 171, 240),
                        2,
                        tipLength=0.15,
                    )

            # Prohibited objects (phones, books, secondary devices)
            # The object worker runs at 1 Hz to save compute, while face models run at 30 Hz.
            # Hold the latest detected objects for 1.2s to render a steady bounding box.
            if snap.objects:
                held_objects = snap.objects
                last_object_time = time.time()
            elif "prohibited_object" not in active_violations and (time.time() - last_object_time > 1.2):
                held_objects = []

            for obj in held_objects:
                ox = int(obj.bbox.x)
                oy = int(obj.bbox.y)
                ow = int(obj.bbox.w)
                oh = int(obj.bbox.h)
                cv2.rectangle(img, (ox, oy), (ox + ow, oy + oh), (68, 68, 239), 2)
                cv2.putText(
                    img,
                    f"{obj.label.upper()} {int(obj.score * 100)}%",
                    (ox, max(18, oy - 6)),
                    cv2.FONT_HERSHEY_SIMPLEX,
                    0.45,
                    (255, 200, 200),
                    1,
                    cv2.LINE_AA,
                )

        # 4. Display frame and handle interactive keys
        cv2.imshow("Vigilo Stream Viewer", img)
        key = cv2.waitKey(1) & 0xFF
        if key in (27, ord("q")):
            break
        elif key == ord("e"):
            pipe.enrol()

finally:
    pipe.stop()
    cv2.destroyAllWindows()

You can also run the full modular viewer script with HUD telemetry cards and violation status pills in examples/live_opencv_hud.py:

python examples/live_opencv_hud.py --source camera:0

Model weights

The neural pipeline uses ONNX Runtime models:

  • Face detection: YuNet (face_detection_yunet_2023mar.onnx)
  • Head pose: MobileNetV3 (headpose_mobilenetv3_small.onnx)
  • Gaze estimation: MobileGaze (mobileone_s0_gaze.onnx)
  • Object detection: YOLOX-Nano (yolox_nano.onnx)

By default, Pipeline(models_dir="models") downloads missing models on first use. You can also download them explicitly:

import vigilo_stream
vigilo_stream.download_models("models")

Alternatively, download them using curl:

mkdir -p models
curl -sSL -o models/face_detection_yunet_2023mar.onnx https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx
curl -sSL -o models/headpose_mobilenetv3_small.onnx https://github.com/yakhyo/head-pose-estimation/releases/download/weights/mobilenetv3_small.onnx
curl -sSL -o models/mobileone_s0_gaze.onnx https://github.com/yakhyo/gaze-estimation/releases/download/weights/mobileone_s0_gaze.onnx
curl -sSL -o models/yolox_nano.onnx https://github.com/Megvii-BaseDetection/YOLOX/releases/download/0.1.1rc0/yolox_nano.onnx

Architecture

Camera / Video File / Image Directory
                 │
                 ▼
  FrameSource (DirectShow / FFmpeg)
                 │
                 ▼
     ArcSwap Latest-Frame Slot  ◄── Zero-copy pointer sharing with NumPy
        ┌────────┴────────┐
        ▼                 ▼
   Face Worker      Object Worker
  YuNet+Pose+Gaze     YOLOX-Nano
        └────────┬────────┘
                 ▼
              Signals ──► FusionEngine ──► Events / Violations

Building from source

Requirements:

  • Rust 1.80 or newer
  • Python 3.9 or newer
  • C++ build tools (MSVC on Windows, GCC/Clang on Linux and macOS)
# Set up a virtual environment and install build tools
uv venv
uv pip install maturin pytest numpy

# Build and install the extension into the active environment
uv run maturin develop

# Run the test suite
uv run pytest -v tests/

Release notes

v1.0.1

  • Updated documentation links and PyPI project URLs.
  • Enhanced README with embedded documentation links.

v1.0.0

  • Added cross-platform on-demand GPU acceleration architecture:
    • Windows: DirectML (DirectX 12) acceleration for NVIDIA, AMD, Intel Arc, and Qualcomm GPUs.
    • Linux: NVIDIA CUDA acceleration.
    • macOS: CoreML / Metal acceleration for Apple Silicon.
  • Introduced dynamic hardware detection (detect_gpu_support()) and on-demand GPU backend downloading (download_gpu_backend()), keeping the default PyPI package lightweight (~18 MB).
  • Added device="auto", device="gpu", and device="cpu" parameters to Pipeline.
  • Added runtime provider inspection via device_info().
  • Added full VitePress documentation website with dark/light themes and published to GitHub Pages.

v0.1.1

  • Added download_models() helper to fetch default ONNX model weights automatically.
  • Enhanced Pipeline to download missing model files automatically on first use (auto_download=True).
  • Added MODEL_URLS mapping and updated documentation.

v0.1.0

  • Initial release of vigilo-stream (with rustream backward-compatibility alias) targeting Python 3.9 through 3.13.
  • Implemented Frame with __array_interface__ and memoryview() support for zero-copy NumPy interop.
  • Implemented FusionEngine with single-frame stepping and deterministic JSONL log replay.
  • Implemented Pipeline context manager wrapping camera capture, detection workers, and event polling.
  • Added data bindings for BBox, FaceDetection, HeadPose, Gaze, ObjectDetection, Signals, Violation, and Event.
  • Multi-platform CI testing across Windows, Ubuntu, and macOS.

License

AGPL-3.0. See LICENSE for details.

Release files for vigilo-stream 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vigilo-stream 1.0.1
File Size Uploaded
vigilo_stream-1.0.1.tar.gz 190.8 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for vigilo-stream 1.0.1
File Interpreter ABI Platform
vigilo_stream-1.0.1-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
vigilo_stream-1.0.1-cp39-abi3-manylinux_2_28_x86_64.whl CPython 3.9 abi3 Linux glibc 2.28+ x86-64 Details
vigilo_stream-1.0.1-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details

Total release size: 28.9 MB

Release files / vigilo_stream-1.0.1.tar.gz

Download URL vigilo_stream-1.0.1.tar.gz
Size 190.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3d30e9c73a2b49840624e472e02573899d790bd1f3595567f606360ab982200d
BLAKE2b-256 checksum
How to use checksums
f317229c01f27d643e06c30e5f2e67492dea5371de9beadcf06b59649b6d644e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / vigilo_stream-1.0.1-cp39-abi3-win_amd64.whl

Download URL vigilo_stream-1.0.1-cp39-abi3-win_amd64.whl
Size 9.2 MB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
e7ec921375071940c967454cffcda8dfa675dfe04f796e2458e99c9114e0d8c4
BLAKE2b-256 checksum
How to use checksums
d128492c318ea2dd301ddf7506490e0d982007a924d246b4ad558a282c65d2d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / vigilo_stream-1.0.1-cp39-abi3-manylinux_2_28_x86_64.whl

Download URL vigilo_stream-1.0.1-cp39-abi3-manylinux_2_28_x86_64.whl
Size 10.7 MB
Tags CPython 3.9 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
b178e28435357d57d16d869dfc7db5a549cdf3e8cbb0fb66e79b6f92334ad276
BLAKE2b-256 checksum
How to use checksums
adb73f6e034c689ca10f8110652389e69a5b9bef6e5c53d264823bf9bef0ce38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / vigilo_stream-1.0.1-cp39-abi3-macosx_11_0_arm64.whl

Download URL vigilo_stream-1.0.1-cp39-abi3-macosx_11_0_arm64.whl
Size 8.9 MB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
a02e97f9b2ed3086c2129feec952137d8586e1200f97477443e362039fb2e7e9
BLAKE2b-256 checksum
How to use checksums
7c774740485fe35f2b2d888a62c587e596c7068a588b327ab6a03dde896a7a63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.1 This release

4 release files

1.0.0

4 release files

0.1.1

4 release files

0.1.0

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page