Switching from Automation Technology C5 to C6 cameras in CVB

Since the release of the Automation Technology C6 series there are some changes required in the image acquisiton method of you CVB application. AT C6 features MultiPart an thus regularly requires the 3rd Generation image acquisition stack of CVB.
The C5 cameras from Automation Technology used to transmitt the acuired data containing Range, Intensity and Scatter data in an interlaced format. In this format a transmitted image would look like this line by line:

Intensity Line 0
Scatter Line 0
Range Line 0
Intensity Line 1
Scatter Line 1
Range Line 1
Intensity Line 2

While this was a fine way of transmitting associated data it was limited to one AOI and all data required the same bit depth.

Using the same acquisition using MultiPart at the C6 the format would look like this while still transmitting all date in one buffer not requiring any prostprocessing to extract the images:

Range Image 16 bpp
Intensity Image 10 bpp
Scatter Image 8 bpp

When now exchanging a C5 by a C6 in a existing system a whole change of the image acquisiton stack would be required and probably an update of the SDK version as well, bringing in multiple uncertainties.
Luckily the C6 offers a “backdoor” to be able to keep using GenICam 1.0 for the image acquisiton which also enables a relativly smooth transition when an exchange of a C5 to a C6 is required in a existing system.

For that the C6 offers the NodeMap parameter Cust::GevSCCFGMultiPart that allows to disable the MultiPart functinality and to use a GenICam 1.0 acquisiton mode, when disabled.
In this case the camera transmitts Range, Intensity and Scatter data of one AOI in a way that the Range data are transmitted in the so called image buffer while Intensity and Scatter are transmitted over image Chunk and can be read from the memory while both keep their own bit depth.
So for exchanging a C5 by C6 camera only requires to exchange the image extraction method after the identical image acquisiton method.
Here is an example how to change the extraction code for Range, Intensity and Scatter.
First the known way on extracting the three images from a C5 image in .NET:

public void ExtractInputImage()
      //Data are transmitted in one single image of the size width * ProfilesPerFrame * 3 * 16 bpp. Alle three images are in 16 bpp.
      RangeImage = new Image(InputImage.Width, NumberOfProfiles, 1, DataTypes.Int16BppUnsigned);
      IntensityImage = new Image(InputImage.Width, NumberOfProfiles, 1, DataTypes.Int16BppUnsigned);
      ScatterImage = new Image(InputImage.Width, NumberOfProfiles, 1, DataTypes.Int16BppUnsigned);

      LinearAccessData<UInt16> imageDataInputImage = InputImage.Planes[0].GetLinearAccess<UInt16>();
      LinearAccessData<UInt16> imageDataRangeImage = RangeImage.Planes[0].GetLinearAccess<UInt16>();
      LinearAccessData<UInt16> imageDataIntensityImage = IntensityImage.Planes[0].GetLinearAccess<UInt16>();
      LinearAccessData<UInt16> imageDataScatterImage = ScatterImage.Planes[0].GetLinearAccess<UInt16>();
      for (int y = 0; y < NumberOfProfiles; y++)
        for (int x = 0; x < InputImage.Width; x++)
          //Iterate through input image by stepping every third line for each image type.
          imageDataIntensityImage[x, y] = imageDataInputImage[x, y*3];
          imageDataScatterImage[x, y] = imageDataInputImage[x, (y*3)+1];
          imageDataRangeImage[x, y] = imageDataInputImage[x, (y*3)+2];

Now reaching the same goal with a C6. Remeber to deactivate MultiPart first:

BooleanNode Multipart = nmp["Cust::GevSCCFGMultiPart"] as BooleanNode;
      Multipart.Value = false;

Now extract the Rangemap image from the raw input image and extract the Intensity and Scatter image from the attached chunk:

public void ExtractIntensityAndScatterFromImageChunk()
      //Datatypes of images vary between Range, Intensity and Scatter.
      RangeImage = InputImage; //Range image is transmitted in the image buffer of the datamessage.
      IntensityImage = new Image(InputImage.Width, InputImage.Height, 1, DataTypes.Int10BppUnsigned);
      ScatterImage = new Image(InputImage.Width, InputImage.Height, 1, DataTypes.Int8BppUnsigned);
      LinearAccessData<UInt16> imageDataIntensityImage = IntensityImage.Planes[0].GetLinearAccess<UInt16>();
      LinearAccessData<byte> imageDataScatterImage = ScatterImage.Planes[0].GetLinearAccess<byte>();

      //Define the size of ChunkData 
      long ImageDataSize = InputImage.Width * InputImage.Height * 2;
      long ChunkSize = PayloadSize - ImageDataSize;

        IntPtr ptrBaseIMG = InputImage.Planes[0].GetLinearAccess().BasePtr;//Get Basepointer of InputImage.
        UInt16* ptrBaseIntensityImage = ((UInt16*)(ptrBaseIMG + (int)ImageDataSize))+4;//8 Byte Offset between Range Image and Intensity Image        
        byte* ptrBaseScatterImageOffset = ((byte*)(ptrBaseIMG + 2*(int)ImageDataSize))+16;//8 Byte Offset between Intensity Image and Scatter Image (+ Offset Range-Intensity)
        //Iterate over memory staring at base pointer address, extracting Intensity and Scatter image.
        for(int x = 0; x < InputImage.Width; x++)
          for(int y = 0; y < InputImage.Height; y++)
            UInt16* ptrCurrIntensityImage = (UInt16*)(ptrBaseIntensityImage + (x+(InputImage.Width * y)));
            imageDataIntensityImage[x, y] = *ptrCurrIntensityImage;
            byte* ptrCurrScatterImage = (byte*)(ptrBaseScatterImageOffset + (x+(InputImage.Width * y)));
            imageDataScatterImage[x, y] = *ptrCurrScatterImage;

For further information on the usage of C5 cameras please refere to this forum post.
In the following posts there will be topics added related to the usage of C6 cameras in CVB.


Performing image acquisition with an Automation Technology C6 camera in CVB.

While using the Chunk Image Mode on the C6 to acquire composite images from the C6 without using MultiPart works fine for existing imaging systems, by creating a new application using C6 cameras the prefered way to acquire images should be the use of MultiPart.

There are multiple advantages the C6 platform has compared to its predecessor the C5 series.
The C6 allows to acquire images from 4 AOIs (Regions) in parallel while at each region iti s possible to extract up to 4 laser line peaks, allowing the full collection of data for (semi-)transparent surfaces. The maximum number of parts in a composite is nine.

The following example in CVB.NET shows how to acquire images with a C6 using GenICam 3.0 MultiPart image Acquisition.

using System;
using Stemmer.Cvb;
using Stemmer.Cvb.GenApi;
using Stemmer.Cvb.Utilities;
using Stemmer.Cvb.Driver;

namespace CVB_Multipart_ATC6
  class Program
    static void Main(string[] args)
      Console.WriteLine("Start Program.");
      DiscoveryInformationList DicoveryList = DeviceFactory.Discover(DiscoverFlags.IgnoreVins);       
      Device device = DeviceFactory.Open(DicoveryList[0],AcquisitionStack.GenTL);      
      Console.WriteLine((device.NodeMaps[NodeMapNames.TLDevice]["Std::DeviceModelName"] as StringNode).Value + " opened.");
      //Multipart Acquisition
      CompositeStream stream = (((GenICamDevice)device).GetStream<CompositeStream>(0));      
      for (int i = 0; i < 10; i++)
        using (Composite composite = stream.Wait(out WaitStatus status))
          using(MultiPartImage image = MultiPartImage.FromComposite(composite))
            using (NodeMapDictionary nodeMaps = NodeMapDictionary.FromComposite(composite))
              int index = 0;
              foreach (var part in image.Parts)
                switch (part)
                  case Image partImage:
                    partImage.Save("Multipartimage_"+ i + "_" + index + ".tif");
                    Console.WriteLine("Composite " + i + " Part " + index + " is an Image");
                  case PfncBuffer buffer:
                    Console.WriteLine("Composite " + i + " Part " + index + " is an PFNCBuffer");
      Console.WriteLine("Program finished");

In case of the C6 all composite parts are images. There are other devices that for example contain a pointcloud inside the composite. In this case the part extraction could look like this:

using (PointCloud pointcloud = PointCloud.FromComposite(composite))
            //Do something