Streaming Images (Experimental)
| The image stream is experimental (from v2.3.0). Its nodes may still change. Use it for evaluation, and verify it with your camera before relying on it. |
capture delivers one image per request, and each request waits until its image has arrived. On
the camera we tested this gives about 34 images per second, well below what the camera itself can
deliver: the next exposure cannot start before the previous image has been read out and sent.
The image stream works the other way round. While it runs, the Cam Driver publishes every image
the camera delivers on <camera name>/control/publish/output/stream/frame. Your program subscribes
to that node and is sent each image as it arrives - with its size, its pixel format and where it came
from, see What each frame carries.
The stream is switched on and off by one function, control/publish: set its inputs, then call
control/publish/execute to apply them. See Running a stream.
The general stream concept - producers, consumers and the lineage of their results - is described in Streams. This page describes the Cam Driver’s stream.
The Cam Driver does not trigger the camera by itself. An image comes when the camera is triggered -
by a signal on its trigger input, or by control/trigger/execute - or, with the camera’s trigger mode
off, as fast as the camera runs. See Triggering the camera.
| Data Layer | Images delivered to a subscriber of control/publish/output/stream/frame |
|---|---|
ctrlX Data Layer (ctrlX OS) |
Every image, in order, as soon as it is published, whatever the subscription’s publish interval. Images are dropped only when the Data Layer falls far behind. |
LumiScan Keryx (PC) |
Every image, in order. The Data Layer drops images only when a subscriber falls far behind. |
Each frame carries its number in the stream, so a subscriber sees a dropped
image as a gap. A read of control/publish/output/stream/frame always returns the latest image.
Addresses
All addresses below start with hdvisionsystems/lumiscan-cam-driver/<camera name>/. They follow
the rule of every execute method: the method owns its nodes, control/publish/execute,
control/publish/input/… and control/publish/output/….
| Address | Type | Default | Meaning |
|---|---|---|---|
|
method |
Applies the inputs below together. Writing an input alone changes nothing. Returns at once: the
stream starts or stops within the Cam Driver’s next cycle, and |
|
|
bool |
|
|
|
bool |
|
|
|
string |
|
The tag on the module’s images: the camera’s step in their lineage carries it, and so do the
results of every application that processes them. Empty: the camera name, for example |
What execute applied is not saved, except the tag: after the Cam Driver starts, the stream is off
until control/publish/execute is called with enable true.
| Address | Type | Meaning |
|---|---|---|
|
bool |
|
|
flatbuffers ( |
Each streamed image: the pixels, their size and format, and the image’s lineage. Subscribe to it to receive the images. See What each frame carries. |
|
uint64 |
Size of the streamed images in pixels, for browsing. Each frame carries its own. |
|
string |
Pixel format of the streamed images, for browsing. Each frame carries its own. |
|
uint64 |
Counters of the current or last stream, see Reading the statistics. |
| Address | Type | Meaning |
|---|---|---|
|
string |
The hash of the settings the images are taken with, as 16 hex digits. See The settings hash. |
|
string |
The tag |
The tag
A changed control/publish/input/tag is saved in the module configuration file, as stream.tag,
before control/publish/execute applies it, the rest of the file as it was. So the images carry it
after a restart too, and control/publish/input/tag starts with it. If the file cannot be written,
control/publish/execute answers DL_CREATION_FAILED, and neither the tag nor the other inputs
change. An unchanged tag is not saved, so a module whose file cannot be written still streams.
The tag is not part of the settings hash: it names the images, it does not change them.
Triggering the camera
Each trigger the camera receives gives one image on control/publish/output/stream/frame. Set the
camera’s trigger mode before you start the stream, through its feature nodes under features or in
a feature file.
| Images come | Camera settings | What to do |
|---|---|---|
On a hardware trigger |
|
Send a pulse to the trigger input for each image. |
On request |
|
Call |
Continuously |
|
Nothing. The camera delivers as fast as it runs, or at its |
A camera without a software trigger mode - a simulated camera playing a folder of images, or a plugin
camera without TriggerSoftware - takes one image for each call to control/trigger/execute as well.
control/trigger/execute answers when the trigger has been sent, not when its image has arrived. A
camera that is still busy with the previous image can ignore a trigger without reporting it, see
For the highest frame rate: TriggerOverlap.
For the highest frame rate: TriggerOverlap
With the GenICam feature TriggerOverlap set to Off, a camera takes the next image only once the
previous one has been read out, and it ignores a trigger that comes earlier. An overlap lets the next
exposure start while the previous image is still read out or sent, which raises the frame rate the
camera reaches.
To stream at the highest frame rate, TriggerOverlap must not be Off. Set it to an overlap
your camera supports - PreviousFrame or ReadOut, see its documentation - in a feature file of the
camera module, one feature per line:
TriggerOverlap PreviousFrame
List the file in the module’s cameras[].featureFiles. The Cam Driver applies it each time it
connects the camera, before acquisition starts, so it holds for capture as well. A feature node
under features works too, but some cameras refuse trigger features while they acquire, and the
value is lost when the camera is connected again.
What each frame carries
control/publish/output/stream/frame holds a FlatBuffers table of type stream_image, registered on
the Data Layer as types/hdvisionsystems/lumiscan-cam-driver/stream_image. Its compiled schema is
installed as bfbs/stream_image.bfbs.
| Field | Meaning |
|---|---|
|
The pixels, row after row, without padding. |
|
The image size in pixels. |
|
The pixel format, for example |
|
Where the image came from. For a camera image this is one step, the camera’s own: * |
The applications that process the stream - AI Object Matching, Calibration - copy the lineage of
what they receive and add a step of their own, with their own tag. Their results therefore still name
the camera image they came from, by producer, run and sequence, and each step says by its tag
which module made it.
The settings hash
setting/settings-hash and the settings_hash of each frame identify the settings the camera
works with. The hash changes when one of them changes:
-
the module settings and the feature files, as the camera was connected with;
-
every camera feature written while the camera is connected, for example
ExposureTimethroughinput/exposure-timeor a feature node underfeatures, with the value the camera accepted; -
the Cam Driver’s version.
Settings that only affect how images are transported, such as the timeout or the packet size, do not
change the hash. The first time a hash appears, the Cam Driver writes the settings it stands for to
settings-history/<camera name>-<hash>.json in its configuration folder, so a hash found in a
result can be looked up later.
| A camera takes the images that are already on their way with the old settings. The images that arrive right after a feature was written can carry the new hash although they were taken before the change. |
What the hash does not tell
-
That, not what. A frame carries the hash only. A different hash says that something the images are taken with changed, not what changed. To find out, compare the two files in
settings-historythat the hashes name, by hand. The files are on the device the Cam Driver ran on. -
Only what goes through the Cam Driver. The hash follows a feature node under
features,input/exposure-time, and the settings and feature files the camera was connected with. It does not follow values the camera changes by itself: withExposureAutoon, the hash follows theExposureAutosetting, not the exposure times the camera picks.
| That the hash follows a camera feature change has so far been tested only in unit tests, with a stand-in for the camera, not with a real camera. Until it has been, do not rely on the camera’s settings hash alone to show that a camera feature changed. |
Running a stream
-
Connect the camera with the
connect-camerahandshake. See Connection Steps. -
Set the camera features you need, such as the exposure time, the trigger mode and
TriggerOverlap, before starting the stream. See Triggering the camera. -
Subscribe to
control/publish/output/stream/frame. -
Set
control/publish/input/enabletotrue, andcontrol/publish/input/keep-latest-outputtotrueif you also want each image onoutput/image. -
Call
control/publish/execute, and wait untilcontrol/publish/output/stream/activeistrue. -
Trigger the camera. Each image arrives on
control/publish/output/stream/frame. -
To stop, set
control/publish/input/enabletofalseand callcontrol/publish/executeagain.control/publish/output/stream/activeturnsfalsewithin the next cycle. -
Before capturing again, wait until
control/capture/readyistrue. See After the stream.
Good to know:
-
The inputs take effect only on
execute, all together. A program can therefore set them in any order, and nothing changes half-way. -
What
executeapplied stays until the nextexecute: the stream runs for as long asenablewas appliedtrueand the camera is connected. Applied while the camera is disconnected, the stream starts as soon as the camera is connected. -
connect-camera,disconnect-cameraandresetstop a running stream. Ifenablewas appliedtrue, the stream starts again once the camera is connected. -
If the stream cannot start,
control/publish/output/stream/activestaysfalseand the reason is written to the log. It is not retried untilcontrol/publish/executeis called again. -
A camera ignores triggers that come faster than it can take images, without reporting it. If
statistics/framesgrows more slowly than the number of triggers you sent, trigger less often or set an overlap, see For the highest frame rate: TriggerOverlap. -
The exposure time on
input/exposure-timeand the inter-packet delay are not applied while the stream runs. Change camera features only while the stream is stopped.
Capture while the stream runs
The stream and capture use the camera in different ways, so they never run at the same time:
-
control/capture/readyisfalsewhile the stream runs, so a program using the handshake waits. -
control/capture/executeanswersDL_INVALID_OPERATION_MODEwhile the stream runs, andDL_TOO_MANY_OPERATIONSwhile the stream is starting or stopping. Do not retryDL_INVALID_OPERATION_MODEin a loop. Stop the stream and wait forcapture/readyinstead. See Execute a Method. -
A capture request that reaches the Cam Driver while the stream runs is refused with status
-3(invalidInput), with the text "capture refused: the image stream (experimental) is active". It does not latch a fault, so noresetis needed. -
A stream that is switched on while a capture runs starts once that capture has finished. It never cuts a capture short.
Streamed images are published on control/publish/output/stream/frame. They reach output/image
only while keep-latest-output is applied true: then each one replaces the image there, and
output/package-identifier counts it as it counts a capture, continuing from the last capture’s
number. save keeps saving the last image taken with capture or load, also then.
After the stream
While streaming, the camera delivers its images in a different acquisition mode from the one
capture uses. To switch back, the Cam Driver disconnects the camera and connects it again when the
stream stops, applying every connection setting as connect-camera does. capture/ready turns
true once that has finished. Until then, capture/execute answers DL_TOO_MANY_OPERATIONS.
Starting and stopping therefore take different times. On the camera we tested:
| Step | Took | Mostly spent on |
|---|---|---|
Starting the stream |
about 0.35 s |
Switching the camera’s acquisition mode. |
Stopping the stream, until |
about 2.2 s |
Connecting the camera again: opening the device (about 0.8 s) and reading all of its GenICam features for the Data Layer (about 0.8 s). |
So switch between streaming and capturing as rarely as you can. A program that needs single images
while streaming can take them from control/publish/output/stream/frame instead of stopping the
stream, or apply keep-latest-output and read output/image when output/package-identifier
changes.
Measured frame rates
These figures were measured with a LUCID PHX016S (1440 x 1080, Mono8, exposure 1 ms) and LumiScan Keryx on one PC. They show the order of magnitude only: your camera, exposure time, network and Data Layer decide what you reach.
They were measured while the Cam Driver still triggered the camera itself, at a fixed rate. A
hardware trigger or a continuously running camera is expected to give the same. Triggering with
control/trigger/execute adds one method call per image; that has not been measured yet.
| Method | Images per second delivered |
|---|---|
|
about 34 |
Stream, |
up to 63, every image delivered |
Stream, |
up to 66, every image delivered |
What the camera itself can deliver ( |
71.7 |
Reading the statistics
The counters, under control/publish/output/stream/statistics/, start from zero each time the stream
starts, and are updated about four times a second while it runs.
| Counter | Meaning |
|---|---|
|
Milliseconds since the stream started, measured by the Cam Driver. Divide a counter by it for a rate. |
|
Complete images the camera delivered. |
|
Images the camera delivered incomplete. They are not published. |
|
Images that never reached the Cam Driver, counted from gaps in the camera’s frame numbers. |
|
Images written to |