Common Vision Blox 14.1
Programming with CVB - First Steps

With Common Vision Blox 13.3 new image and data acquisition interfaces have been introduced. The GenTL interface now also supports acquisition of multiple streams from one device, acquisition of structured data streams that contain multiple logical parts (enabling the acquisition of 3D point clouds directly from devices that support this) and acquisition into user-specified destination buffers. A detailed description how to acquire images and point clouds can be found in the document 3rd Generation Acquisition Interfaces. If you are still using the CVB vin driver and like to migrate you code to the up-to-date GenTL stack, you will find a step-by-step guide for all supported programming languages in the document Migration Guide for the Acquisition Stacks.

The Common Vision Blox SDK is provided for three programming languages. Further information can be found under the following links:

Basic Image Acquisition

After sucessfully installing CVB and configuring you camera, you can start programming with CVB.

In this section you will find code examples for each supported programming language (C++, .Net and python), covering the following topics:

(1) Setting GenICam Driver Options
(2) Getting and Setting Nodemap Features
(3) Getting an Image
(4) Accessing Pixel Values

(1) Setting GenICam Driver Options

The following code snippet shows how to set specific GenICam driver options. A list of all driver options can be found in this table.

When programming in C++ use function SetParameter().
// Discover all devices with the search criteria defined by the DiscoverFlags
auto devices = Cvb::DeviceFactory::Discover(Cvb::DiscoverFlags::IgnoreVins);
// Set number of buffers allocated at startup
devices.at(0).SetParameter(CVB_LIT("NumBuffer"), CVB_LIT("10"));
static std::vector< DiscoveryInformation > Discover()
In .Net use SetParameter().
// Discover all devices with the search criteria defined by the DiscoverFlags
using (var devices = DeviceFactory.Discover(DiscoverFlags.IgnoreVins))
{
// Set number of buffers allocated at startup
devices[0].SetParameter("NumBuffer", "10");
}
In python use set_parameter().
# Discover all devices with the search criteria defined by the DiscoverFlags
devices = cvb.DeviceFactory.discover_from_root(cvb.DiscoverFlags.IgnoreVins)
# Set number of buffers allocated at startup
devices[0].set_parameter("NumBuffer", "10")
List[cvb.DiscoveryInformation] discover_from_root(int flags=cvb.DiscoverFlags.FindAll, int time_span=300)


(2) Getting and Setting Nodemap Features

To access a specific nodemap use the according get function:

C++ C# Python
Cvb::Device::NodeMap() Cvb.Device.NodeMaps cvb.Device.node_maps

In the following table all available nodemaps and their ID to be used in the get function is listed:

Nodemap / Name C++ C# Python
DevicePort / Device Cvb::NodeMapID::Device NodeMapNames.Device cvb.NodeMapID.Device
DeviceTLPort / TLDevice Cvb::NodeMapID::TLDevice NodeMapNames.TLDevice cvb.NodeMapID.TLDevice
TLPort / TLSystem Cvb::NodeMapID::System NodeMapNames.System cvb.NodeMapID.System
DataStreamPort / TLDatastream Cvb::NodeMapID::DataStream NodeMapNames.DataStream cvb.NodeMapID.DataStream
FactoryPort / TLFactory Cvb::NodeMapID::Factory NodeMapNames.Factory cvb.NodeMapID.Factory
InterfacePort / TLInterface Cvb::NodeMapID::Interface NodeMapNames.Interface cvb.NodeMapID.Interface

The following code snippet shows examplarily how to get and set the device nodemap feature "Std::ExposureTime".

#include <cvb/device_factory.hpp>
#include <cvb/genapi/node_map_enumerator.hpp>
// discover devices
auto devices = Cvb::DeviceFactory::Discover(Cvb::DiscoverFlags::IgnoreVins);
// open device
auto device = Cvb::DeviceFactory::Open<Cvb::GenICamDevice>(devices.at(0).AccessToken(), Cvb::AcquisitionStack::GenTL);
// list nodemaps
auto nodemaps = device->NodeMaps();
for (auto it = nodemaps.begin(); it != nodemaps.end(); ++it)
std::cout << it->first << std::endl;
// change node for exposure time in device node map
auto nodemap = device->NodeMap(Cvb::NodeMapID::Device);
auto node = nodemap->Node<Cvb::FloatNode>("Std::ExposureTime");
node->SetValue(node->Max() / 2.0);
std::cout << "Exposure time set to: " << node->Value() << " " << node->Unit() << "\n";
The exposure time is a Cvb::FloatNode. Be careful, that you cast your node to the correct node class.
// discover devices
using (var devices = DeviceFactory.Discover(DiscoverFlags.IgnoreVins))
{
// open first device
using (var device = DeviceFactory.Open(devices[0], AcquisitionStack.GenTL))
{
// change node for exposure time in device node map
var nodes = device.NodeMaps[NodeMapNames.Device];
var node = nodes["Std::ExposureTime"] as FloatNode;
node.Value = node.Max / 2;
}
}
static Device Open(DiscoveryInformation info, AcquisitionStack acquisitionStack=AcquisitionStack.PreferVin)
static DiscoveryInformationList Discover()
The exposure time is a FloatNode. Be careful, that you use the correct node class.
import os
devices = cvb.DeviceFactory.discover_from_root(cvb.DiscoverFlags.IgnoreVins)
with cvb.DeviceFactory.open(devices[0].access_token, cvb.AcquisitionStack.GenTL) as device:
# change node for exposure time in device node map
node_map = device.node_maps[cvb.NodeMapID.Device]
exposure_node = node_map["Std::ExposureTime"]
exposure_node.value = exposure_node.max / 2
print("Exposure time set to: " + str(exposure_node.value) + " " + exposure_node.unit)
Union[cvb.GenICamDevice, cvb.VinDevice, cvb.EmuDevice, cvb.VideoDevice, cvb.NonStreamingDevice] open(str provider, int acquisition_stack=cvb.AcquisitionStack.PreferVin)
A detailed description of the nodemaps in general can be found in the section Nodemap.

(3) Acquiring an Image

This code example shows how to acquire and save an image.
#include <cvb/global.hpp>
#include <cvb/device_factory.hpp>
#include <cvb/driver/image_stream.hpp>
// discover devices
auto devices = Cvb::DeviceFactory::Discover(Cvb::DiscoverFlags::IgnoreVins);
// open first device
auto device = Cvb::DeviceFactory::Open<Cvb::GenICamDevice>(devices.at(0).AccessToken(), Cvb::AcquisitionStack::GenTL);
// start streaming
auto dataStream = device->Stream<Cvb::ImageStream>();
dataStream->Start();
// acquire data
Cvb::WaitStatus waitStatus;
Cvb::NodeMapEnumerator enumerator;
std::tie(image, waitStatus, enumerator) = dataStream->WaitFor(std::chrono::milliseconds(3000));
image->Save("test.bmp");
// stop streaming
if(!dataStream->TryAbort())
std::cout << "Error: Acquisition has not been succesfully stopped.\n";
WaitStatus
std::shared_ptr< Image > ImagePtr
// discover devices
using (var devices = DeviceFactory.Discover(DiscoverFlags.IgnoreVins))
{
// open first device
using (var device = DeviceFactory.Open(devices[0], AcquisitionStack.GenTL))
{
// start streaming
var stream = ((GenICamDevice)device).GetStream<ImageStream>(0);
stream.Start();
// acquire data
WaitStatus status;
using (var image = stream.Wait(out status))
{
image.Save("test.bmp");
}
// stop streaming
stream.Abort();
}
}
import cvb
devices = cvb.DeviceFactory.discover_from_root(cvb.DiscoverFlags.IgnoreVins)
with cvb.DeviceFactory.open(devices[0].access_token, cvb.AcquisitionStack.GenTL) as device:
stream = device.stream(cvb.ImageStream)
stream.start()
image, status, node_maps = stream.wait()
if status == cvb.WaitStatus.Ok:
image.save("test.bmp")
stream.abort()


(4) Accessing Pixel Values

In general the pixel access depends on the underlying image layout. Therefore CVB provides the fast linear access for images with a linear memory representation and a slower VPAT access for all other memory layouts. Detailed information about the memory layouts can be found in the manual for the ImageManager. In C++ and python a convenient image access is provided by CVB which is independent from the memory layout.

In C++ the most convenient way to access pixel values is using Cvb::Vist(). Cvb::Vist() accepts as input an image plane and a lambda function or a function object where the operation to be executed on the image pixel is impelemented. The only pre-requisite is, that the lambda function can deal with a Cvb::Block object as shown in the example below.
A detailed description of Cvb::Vist() and more code examples can be found here.
#include <cvb/block.hpp>
auto image = ...;
// pixel access using a lamda function and Cvb::Visit()
Cvb::Visit([](auto block)
{
// loop over whole image
for (int y = 0; y < block.Height(); ++y) {
for (int x = 0; x < block.Width(); ++x) {
block(x, y) /= 2; // divide pixel value by 2
}
}
}
, image->Plane(0));
(1) Always use the fast linear access, if the image layout allows it.

(2) If the linear access is not possible, use the slower VPAT access. Check the data type of your plane with plane.DataType.BytesPerPixel and choose the appropriate type of the VPAT access.

(3) It is highly recommended to store image properties like height and width to separate variables, as they are not stored to variables in the Image class. Instead a function is called on the image getting the variables.
using Stemmer.Cvb;
var image = ...;
// get pixel access to first plane
var height = image.Height; // (3)
var width = image.Width;
var plane = image.Planes[0];
// try to get linear access, if it is supported by
// the image layout (fastest access)
LinearAccessData linearAccess;
if (plane.TryGetLinearAccess(out linearAccess)) // (1)
{
unsafe
{
byte* pBase = (byte*)linearAccess.BasePtr;
var yInc = (int)linearAccess.YInc;
var xInc = (int)linearAccess.XInc;
for (int y = 0; y < height; y++)
{
byte* pLine = pBase + y * yInc;
for (int x = 0; x < width; x++)
{
byte* pPixel = pLine + x * xInc;
*pPixel /= 2;
}
}
}
}
// if linear access not possible, use VPAT access (slow)
else
{
var vpatAccess = plane.GetVPATAccess<byte>(); // (2)
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
vpatAccess[x, y] /= 2;
}
}
(1) First you convert your image in a numpy array using cvb.as_array(). If you like to modify your image values, you need to set the copy flag to False. In this case the data is mapped and you can work directly on the array.

(2) But attention: The mapping is not supported for all image layouts! Therefore you have to check, whether the mapping was sucessful. If it was not sucessful, set the copy flag to False. In this case, you work on the copied data and not on the original image.

(3) Then you can modify your image pixels using the numpy array.
import cvb
image = ...
# copy=False is default, but just a request
np_array = cvb.as_array(image, copy=False) # (1)
if np_array.flags["OWNDATA"]: # (2)
raise RuntimeError("cannot map to numpy array")
# pixel access
print("Modifying pixel data via numpy array.")
np_array[83 : 108, 48 : 157] = 0 # (3)
numpy.array as_array(Any buffer, bool copy=False)

Further Examples

INotify Interface

INotify Interface - Callback Functions For Events

This interface allows to register callback functions for events like disconnect/reconnect or events generated by the device. In case such an event carries additional data, it is passed to the callback function.

auto path = Cvb::InstallPath();
path += CVB_LIT("Drivers/GenICam.vin");
path = Cvb::ExpandPath(path);
auto device = Cvb::DeviceFactory::Open(path);
std::cout << "ok" << std::endl;
device->RegisterConnectionStateChangedEvent([device]() {
// The device can only be captured when using lambda
if (device->ConnectionState() == Cvb::ConnectionState::Connected)
std::cout << "Connected" << std::endl;
else
std::cout << "Disconnected" << std::endl;
});
while (true)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
static std::shared_ptr< T > Open(const String &provider, AcquisitionStack acquisitionStack=AcquisitionStack::PreferVin)
var device = DeviceFactory.Open("GenICam.vin");
device.Notify[NotifyDictionary.DeviceDisconnected].Event += Disconnect;
device.Notify[NotifyDictionary.DeviceReconnect].Event += Reconnect;
private static void Disconnect(object sender, NotifyEventArgs e)
{
Console.WriteLine("Device disconnected");
// do stuff
}
private static void Reconnect(object sender, NotifyEventArgs e)
{
Console.WriteLine("Device reconnected");
// do stuff
}
NotifyDictionary Notify
import os
import time
import cvb
def connection_changed(dev: cvb.Device):
print("Connection state changed: ")
print(str(dev.connection_state))
device = cvb.DeviceFactory.open(os.path.join(cvb.install_path(), "drivers", "GenICam.vin"), port=0)
device.register_connection_state_changed_event(connection_changed)
while True:
time.sleep(1)
str install_path()

Sample Programs

Example programs in C++, .Net and python can be found under:

  • %CVB%%\Tutorial (Windows)
  • /opt/cvb/tutorial (Linux)

or in the CVB online documentation: