Architecture#

QVideo is organized into four layers. Each layer depends only on the layers below it, so individual components can be used in isolation.

┌──────────────────────────────────────────────────┐
│  QCamcorder  Demo  FilterDemo  ROIDemo           │  application layer
│  TrackpyDemo  YoloDemo  CompositeDemo            │
├──────────────┬───────────────────────────────────┤
│  QCameraTree │  QVideoScreen  QFilterBank  DVR   │  UI layer
├──────────────┴───────────────────────────────────┤
│  QVideoSource  (QThread)                         │  threading layer
├──────────────────────────────────────────────────┤
│  QCamera  (hardware abstraction)                 │  camera layer
└──────────────────────────────────────────────────┘

Camera layer — QVideo.lib.QCamera#

QCamera is the abstract base for all cameras. Subclasses implement three methods:

  • _initialize() -> bool — open the device, call registerProperty() / registerMethod() for every adjustable parameter, return success.

  • _deinitialize() — release the device.

  • read() -> (bool, ndarray | None) — capture one frame.

registerProperty() stores a property spec in self._properties. get(), set(), and execute() all route through this dict under self.mutex. Attribute access (camera.fps) delegates to registered getters via __getattr__, so subclasses need no explicit Python properties for camera parameters.

Threading layer — QVideo.lib.QVideoSource#

QVideoSource wraps a camera in a QThread, calling camera.saferead() in a loop and emitting newFrame(ndarray). It is the standard way to drive a camera from the GUI thread.

UI layer#

QCameraTree is a pyqtgraph.ParameterTree widget that reads camera._properties to auto-build a control panel — no manual UI code is needed per camera.

QVideoScreen displays live frames from a QVideoSource. It maintains the video aspect ratio by resizing its containing window whenever the frame dimensions change. After each displayed frame it emits newFrame carrying either the filtered video frame or, when composite is enabled, the rendered scene (video + overlays) as an RGBA array. Its fps property reports the effective display rate, so the screen can be used directly as a DVR source for composite recording. Setting colormap applies a false-color LUT to grayscale frames at display time without affecting the underlying frame data.

QFilterBank and VideoFilter provide a composable image-processing pipeline that sits between a source and a display widget.

Application layer#

QCamcorder composes a QVideoScreen, QCameraTree, and QDVRWidget into a single reusable widget.

The demos package provides ready-to-run applications built on the framework, following two inheritance chains:

Camera backends#

Each backend lives in cameras/<Name>/ and follows the pattern:

  • Q<Name>Camera — subclasses QCamera.

  • Q<Name>Source — subclasses QVideoSource.

  • Q<Name>Tree — subclasses QCameraTree when extra UI logic is needed.

QOpenCVTree (-c) probes the connected device at startup to discover its supported resolutions and actual maximum frame rates, then presents a "W×H @ N Hz" dropdown. Selecting an entry atomically updates width, height, and frame rate on the live device without stopping the video source. Width, height, and fps are shown as read-only fields; all format changes go through the dropdown. When probing yields no format information the dropdown is omitted and width/height are displayed as plain read-only values.

Hardware-specific packages are soft dependencies: the import is wrapped in try/except (ImportError, ModuleNotFoundError).

GenICam cameras (Genicam, Flir, Vimbax) discover their GenTL producer .cti file at runtime via the GENICAM_GENTL64_PATH environment variable set by the manufacturer’s SDK installer.

Noise is the reference implementation — no hardware required, used as a model for tests and for verifying the framework.