cristhian 3c0e0f327d
validate / validate (push) Successful in 59m15s
add dora-webcam node
Captures frames from a V4L2 camera and publishes them as std/media/v1/Image:
an Arrow struct of width, height, encoding and data, so a receiver needs
nothing from the message metadata. For a compressed encoding the recorded
dimensions are the image's, not the length of the payload.

Based on opencv-video-capture from dora-hub, with three changes. The output is
the typed image struct rather than a flat UInt8 buffer plus metadata. Camera
selection by id reads /dev/v4l/by-id/ only; the upstream macOS system_profiler
and Windows PowerShell enumeration is not carried. CAPTURE_FRAMES bounds a run
so a test ends on its own, in place of upstream's ten-second CI cutoff -- a
timeout would have made the CI step exit non-zero.

CI=true keeps a failed capture from raising, publishing a labelled black frame
instead, which is what lets dataflow.yml run on a machine with no camera.

Two dataflows, run one at a time, as dora has no cross-dataflow wiring:
dataflow.yml is the camera alone and the CI gate; dataflow_view.yml wires it
into dora-image-view and is not run by CI, since it fetches that node over the
network and needs a tag to exist there.
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00
2026-09-15 06:04:25 -03:00

dora-webcam

Dora node that captures frames from a webcam and publishes them as std/media/v1/Image. A source for a vision pipeline: it takes a tick and produces frames.

Based on opencv-video-capture from dora-hub, brought up to the dora 1.0 node contract, narrowed to Linux camera selection, and changed to publish the typed Arrow image struct rather than a flat buffer plus metadata.

Usage

Add to your dataflow:

nodes:
  - id: camera
    git: ssh://git@gitea.liria.cl:5001/liria/dora-webcam.git
    tag: v0.1.0
    build: pip install .
    path: dora-webcam
    inputs:
      tick: dora/timer/millis/33
    outputs:
      - image

Run the dataflow with --uv (dora run dataflow.yml --uv) so the node gets its own managed environment.

Runnable dataflows

Two dataflows live here, run one at a time — dora has no cross-dataflow wiring, so each declares the camera itself:

Run either with dora run <file> --uv:

File Contents
dataflow.yml The camera alone
dataflow_view.yml The camera into dora-image-view

dataflow_view.yml is the end-to-end check of the pair: it wires this node's image output into the viewer's image input, so a window opens showing the webcam. It fetches the viewer over the network, which is why CI validates and runs dataflow.yml only.

Output

Output Payload Metadata
image std/media/v1/Image struct none — the struct describes itself

The struct carries width: UInt32, height: UInt32, encoding: Utf8 and data: LargeBinary, so a receiver needs nothing from the message metadata. For a compressed encoding, width and height are the image's dimensions, not the length of data.

Inputs

Input Description
tick Publishes one frame. Wire to dora/timer/millis/N; the node stops when it closes.

Environment variables

Variable Default Meaning
CAMERA_ID unset Substring of a /dev/v4l/by-id/ entry. Wins over CAPTURE_PATH.
CAPTURE_PATH 0 Device path (/dev/video1) or index. A numeric string is an index.
IMAGE_WIDTH native Requested capture width; frames are also resized to it.
IMAGE_HEIGHT native Requested capture height; frames are also resized to it.
ENCODING bgr8 One of bgr8, rgb8, yuv420, jpeg/jpg/jpe, png, bmp, webp.
JPEG_QUALITY 95 Quality 0-100, used only by the JPEG encodings.
FLIP unset VERTICAL, HORIZONTAL or BOTH.
CAPTURE_FRAMES 0 Stop after this many frames. 0 runs until the tick closes.

Resizing only happens when both IMAGE_WIDTH and IMAGE_HEIGHT are set.

CAMERA_ID is worth preferring on a robot: a by-id name is stable across reboots and across which USB port the camera is in, and an index is not.

CI=true makes a failed read publish a labelled black frame instead of raising, which is what lets the dataflow run on a machine with no camera. It is not a configuration knob.

Build

uv sync

Every dependency ships prebuilt wheels for linux-x86_64, linux-aarch64 and macos-arm64, so the node installs with no system packages and no compiler.

Requirements: dora 1.0+ (verified with dora-cli 1.0.1), Python 3.11+, uv, and a V4L2 camera. On a bare Linux image opencv-python also needs the libGL shared libraries (libgl1, libglib2.0-0), which it links at import time.

Test

uv run pytest

Unit tests over device resolution, encoding and the image struct; they need neither a camera nor a dataflow. The headless dataflow.yml run covers the wiring and the event loop.

Camera selection on other platforms

Selecting by CAMERA_ID reads /dev/v4l/by-id/, so it is Linux only. Upstream also enumerates cameras through macOS system_profiler and Windows PowerShell; that is not carried here. CAPTURE_PATH works everywhere.

S
Description
Dora node that captures frames from a webcam and publishes them as std/media/v1/Image
Readme
133 KiB
v0.1.1
Latest
2026-09-15 09:50:17 +00:00
Languages
Python 100%