Application Programming Interface

In the introduction we had a quick glance over the basic API used to communicate with a camera. Now we will go into more detail and explain required background to understand the execution model.

Instantiating cameras

We have already seen how to instantiate a camera object from a name. If you have more than one camera connected to a machine, you will most likely want the user decide which to use. To do so, you can enumerate all camera strings with uca_plugin_manager_get_available_cameras:

GList *types;

types = uca_plugin_manager_get_available_cameras (manager);

for (GList *it = g_list_first (types); it != NULL; it = g_list_next (it))
    g_print ("%s\n", (gchar *) it->data);

/* free the strings and the list */
g_list_foreach (types, (GFunc) g_free, NULL);
g_list_free (types);

Errors

All public API functions take a location of a pointer to a GError structure as a last argument. You can pass in a NULL value, in which case you cannot be notified about exceptional behavior. On the other hand, if you pass in a pointer to a GError, it must be initialized with NULL so that you do not accidentally overwrite and miss an error occurred earlier.

Read more about GErrors in the official GLib documentation.

Recording

Recording frames is independent of actually grabbing them and is started with uca_camera_start_recording. You should always stop the recording with ufo_camera_stop_recording when you finished. When the recording has started, you can grab frames synchronously as described earlier. In this mode, a block to uca_camera_grab blocks until a frame is read from the camera. Grabbing might block indefinitely, when the camera is not functioning correctly or it is not triggered automatically.

Triggering

libuca supports three trigger sources through the “trigger-source” property:

  1. UCA_CAMERA_TRIGGER_SOURCE_AUTO: Exposure is triggered by the camera itself.
  2. UCA_CAMERA_TRIGGER_SOURCE_SOFTWARE: Exposure is triggered via software.
  3. UCA_CAMERA_TRIGGER_SOURCE_EXTERNAL: Exposure is triggered by an external hardware mechanism.

With UCA_CAMERA_TRIGGER_SOURCE_SOFTWARE you have to trigger with uca_camera_trigger:

/* thread A */
g_object_set (G_OBJECT (camera),
              "trigger-source", UCA_CAMERA_TRIGGER_SOURCE_SOFTWARE,
              NULL);

uca_camera_start_recording (camera, NULL);
uca_camera_grab (camera, buffer, NULL);
uca_camera_stop_recording (camera, NULL);

/* thread B */
uca_camera_trigger (camera, NULL);

Moreover, the “trigger-type” property specifies if the exposure should be triggered at the rising edge or during the level signal.

Grabbing frames asynchronously

In some applications, it might make sense to setup asynchronous frame acquisition, for which you will not be blocked by a call to libuca:

static void
callback (gpointer buffer, gpointer user_data)
{
    /*
     * Do something useful with the buffer and the string we have got.
     */
}

static void
setup_async (UcaCamera *camera)
{
    gchar *s = g_strdup ("lorem ipsum");

    g_object_set (G_OBJECT (camera),
                  "transfer-asynchronously", TRUE,
                  NULL);

    uca_camera_set_grab_func (camera, callback, s);
    uca_camera_start_recording (camera, NULL);

    /*
     * We will return here and `callback` will be called for each newo
     * new frame.
     */
}

Bindings

Since version 1.1, libuca generates GObject introspection meta data if g-ir-scanner and g-ir-compiler can be found. When the XML description Uca-x.y.gir and the typelib Uca-x.y.typelib are installed, GI-aware languages can access libuca and create and modify cameras, for example in Python:

from gi.repository import Uca

pm = Uca.PluginManager()

# List all cameras
print(pm.get_available_cameras())

# Load a camera
cam = pm.get_camerav('pco', [])

# You can read and write properties in two ways
cam.set_properties(exposure_time=0.05)
cam.props.roi_width = 1024

Note, that the naming of classes and properties depends on the GI implementation of the target language. For example with Python, the namespace prefix uca_ becomes the module name Uca and dashes separating property names become underscores.

Integration with Numpy is relatively straightforward. The most important thing is to get the data pointer from a Numpy array to pass it to uca_camera_grab:

import numpy as np

def create_array_from(camera):
    """Create a suitably sized Numpy array and return it together with the
    arrays data pointer"""
    bits = camera.props.sensor_bitdepth
    dtype = np.uint16 if bits > 8 else np.uint8
    a = np.zeros((cam.props.roi_height, cam.props.roi_width), dtype=dtype)
    return a, a.__array_interface__['data'][0]

# Suppose 'camera' is a already available, you would get the camera data like
# this:
a, buf = create_array_from(camera)
camera.start_recording()
camera.grab(buf)

# Now data is in 'a' and we can use Numpy functions on it
print(np.mean(a))

camera.stop_recording()

Integrating new cameras

A new camera is integrated by sub-classing UcaCamera and implement all virtual methods. The simplest way is to take the mock camera and rename all occurences. Note, that if you class is going to be called FooBar, the upper case variant is FOO_BAR and the lower case variant is foo_bar.

In order to fully implement a camera, you need to override at least the following virtual methods:

  • start_recording: Take suitable actions so that a subsequent call to grab delivers an image or blocks until one is exposed.
  • stop_recording: Stop recording so that subsequent calls to grab fail.
  • grab: Return an image from the camera or block until one is ready.

Asynchronous operation

When the camera supports asynchronous acquisition and announces it with a true boolean value for "transfer-asynchronously", a mechanism must be setup up during start_recording so that for each new frame the grab func callback is called.

Cameras with internal memory

Cameras such as the pco.dimax record into their own on-board memory rather than streaming directly to the host PC. In this case, both start_recording and stop_recording initiate and end acquisition to the on-board memory. To initiate a data transfer, the host calls start_readout which must be suitably implemented. The actual data transfer happens either with grab or asynchronously.