Controls API

Quarklib provides high-level access to V4L2 camera controls through a hierarchy of semi-automatically defined types that take care of value conversion and validation.

Finding controls

The entry-point to this api is the quarklib.camera.Camera.controls property available on any quarklib.camera.Camera instance. To print an overview of the available controls, their formats, and their current values, run the following code snippet:

from quarklib.camera import Camera

cam = Camera()
print(cam.controls)

The output should look similar to the following:

qtec_default_controls:
  digital_gain:
    red (scalar, FloatFormatter): 1.0
    green (scalar, FloatFormatter): 1.0
    blue (scalar, FloatFormatter): 1.0
  ...
  flip:
    horizontal (scalar, BoolFormatter): False
    vertical (scalar, BoolFormatter): False
  binning:
    horizontal (scalar, IntFormatter): 1
    vertical (scalar, IntFormatter): 1
  pattern_generator (scalar, MenuFormatter): Off

To locate a specific control, either find it in the dynamic overview or look at Controls which contains a detailed reference of the full set of controls.

Caution

Depending on your use of camera family and bitstream, all controls may not be available for your setup. Application code has the responsibility of ensuring that invalid access is handled appropriately. Accessing a non-existent control results in an AttributeError.

Using controls

All controls inherit from quarklib.controls.IControl which defines an interface for reading/writing values to that control. Implementations are grouped into two categories, quarklib.controls.DriverControl and quarklib.controls.GroupControl. The former covers controls that have a V4L2 equivalent while the latter covers groupings of other controls.

Driver Controls

A driver control wraps a specific V4L2 control but provides automatic format conversion and validation as part of its definition. The quarklib.controls.DriverControl.formatter attribute, which is an instance of the quarklib.controls.formatters.AbstractFormatter class, determines what values are accepted by the control and how they are converted to/from the format expected by the underlying V4L2 control.

The currently supported formatters are:

Group Controls

Control groups provide high-level and hierarchical access to other controls. Groups contain multiple child controls that can be accessed either through attributes (e.g. cam.controls.digital_gain.red) or through the group’s own read/write methods.

The value type and structure returned from quarklib.controls.GroupControl.read() and expected as the argument in quarklib.controls.GroupControl.write() depends on the specific class of group used.

Quarklib provides the following built-in group types (custom ones can be made by subclassing quarklib.controls.GroupControl):

Value validation

All controls perform value validation when writing values. For groups, this is delegated to its children and for quarklib.controls.DriverControl instances, it is performed by the type’s quarklib.controls.formatters.AbstractFormatter instance.

Since validation is performed when calling quarklib.controls.IControl.write(), it may throw a ValueError. Reading does not need to validate values, since all written values are assumed to be validated, but may still throw qamlib.V4L2Exception when IO-related errors occur.

Implementation details

The base type is built at runtime when a new quarklib.camera.Camera instance is created. The exact contents depend on the capabilities of the camera (sensor family, bitstream, and driver version) but the default configuration using the draco-tokyo bitstream is documented as quarklib.controls.definitions.QtecDefaultControls.

The main reasons for creating a new class for each control is clarity and for safety. Making a type for each V4L2 control makes it more readily identifiable without having to look at instance variables and it also makes it less likely that controls are used incorrectly since it is possible to specify its type at the usage site. Since Python is dynamically typed, it is of course possible to violate type hints but this is now the responsibility of the caller instead of the callee, thus eliminating the need to verify control formats at each interface point.

The reason the classes are created dynamically is that many of the control parameters can be read from the V4L2 driver itself thus eliminating the need to manually copy these parameters.

Although this approach makes it impossible to inspect the source of each created class, the documentation still provides documentation for generated classes in Controls. These standard control types are created using V4L2 parameters saved from a specific camera configuration (currently the IMX421 with the draco-tokyo bitstream).

Building custom controls

You can easily replace the default Qtec control hierarchy in the quarklib.camera.Camera constructor with a custom quarklib.controls.GroupControl class. This allows you to build a custom interface to a camera. This guide will also show you how to extend the default type such that the resulting Camera instance is still compatible with the rest of the quarklib API.

Controls are built using the helper classes provided in quarklib.controls.builders. These each provide a set of class methods that simplify the creation of new control hierarchies. For examples of how to use them, check the quarklib.controls.definitions source code.