How to update driver memory after changing camera parameters

Hey, wrote a little C# program to demonstrate how to update the image data if the changed parameter concerns the memory of an image. We need to update the memory allocated by the driver to be able to interpret the new image data.

I posted this in the general topic because I will submit the code translated into Python and C++ later.


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

namespace PixelFormat
{
  class Program
  {
    static void Main(string[] args)
    {
      Device device = DeviceFactory.Open("GenICam.vin");
      NodeMap deviceNodeMap = device.NodeMaps[NodeMapNames.Device];

      // Stream need to be stopped when setting image width or similar
      // as this values are not accessible while grabbing. Thus we are using GetSnapshot rather than 
      // manually start the stream, get an image and then stop the stream to be able to make further changes on the image
      WriteImageData(device.Stream.GetSnapshot());

      Console.WriteLine("Now changing width in nodemap and updating image rect");

      var oldWidth = device.DeviceImage.Width;
      SetIntegerNodeMapValue(deviceNodeMap["Width"] as IntegerNode, 2000);

      // As we changed parameters that directly concerns the memory for an image we have to update 
      // the memory allocated by the driver to be able to correctly interprete the new image data
      device.ImageRect.Update(DeviceUpdateMode.UpdateDeviceImage);

      WriteImageData(device.Stream.GetSnapshot());

      // Set the width back to the old value
      SetIntegerNodeMapValue(deviceNodeMap["Width"] as IntegerNode, oldWidth);

      // keep console alive
      Console.ReadKey();
    }

    private static void WriteImageData(Image toWriteDataFrom)
    {
      Console.WriteLine("Width of image: " + toWriteDataFrom.Width);
    }

    private static void SetIntegerNodeMapValue(IntegerNode fromNode, int toValue)
    {
      fromNode.Value = toValue;
      Console.WriteLine("Set " + fromNode.Name + " to value: " + fromNode.Value);
    }
  }
}

4 Likes

Good job @silas!

However there is one thing worth mentioning: In case of image size changing (as in your example), the UpdateDeviceImage method can be avoided if the ChangeSize method of the IImageRect Interface is used :nerd_face:

3 Likes

Thank you!

I wanted to show a way that works with bitdepth and colorformat and I wasn’t sure if this would work. But thanks for the mention. :smiley:

2 Likes

Hey, here is my code snippet rewritten in python. The C++ version is coming as soon as possible.

import cvb
import os
import cvb.foundation

def WriteImageData(toWriteDataFrom):
        print("Width of image:", toWriteDataFrom.width)

def SetIntegerNodeMap(fromNode, toValue):
        fromNode.value = toValue
        print("Set", fromNode.name, " to value:", fromNode.value)

device = cvb.DeviceFactory.open(os.path.join(cvb.install_path(), "drivers", "GenICam.vin"))
deviceNodeMap = device.node_maps["Device"]

WriteImageData(device.device_image)

print("Now changing width in nodemap and updating image rect")

oldWidth =  deviceNodeMap["Width"].value
SetIntegerNodeMap(deviceNodeMap["Width"], 2000)

# As we changed parameters that directly concerns the memory for an image we have to update
# the memory allocated by the driver to be able to correctly interprete the new image data
device.image_rect.update(0)

WriteImageData(device.device_image)

# Set the width back to the old value
SetIntegerNodeMap(deviceNodeMap["Width"], oldWidth)
2 Likes

After some delay, here is my code snipped translated into C++.

#include <iostream>
#include <cvb/device_factory.hpp>
#include <cvb/utilities/system_info.hpp>
#include <cvb/driver/stream.hpp>
#include <cvb/driver/image_rect.hpp>

using namespace std;
using namespace Cvb;

void WriteImageData(DeviceImagePtr toWriteDataFrom) {
	cout << "Width of image: " << toWriteDataFrom->Width() << endl;
}

void SetIntegerNodeMapValue(shared_ptr<IntegerNode> fromNode, int toValue) {
	fromNode->SetValue(toValue);
	cout << "Set " << fromNode->Name() << " to value: " << fromNode->Value() << endl;
}

int main() {
	auto device = DeviceFactory::Open(ExpandPath(InstallPath() + CVB_LIT("drivers/GenICam.vin")), AcquisitionStack::Vin);
	auto deviceNodeMap = device->NodeMap(CVB_LIT("Device"));

	WriteImageData(device->DeviceImage());

	cout << "Now changing width in nodemap and updating image rect" << endl;

	auto oldWidth = device->DeviceImage()->Width();
	SetIntegerNodeMapValue(deviceNodeMap->Node<IntegerNode>("Width"), 2000);

	// As we changed parameters that directly concerns the memory for an image we have to update 
	// the memory allocated by the driver to be able to correctly interprete the new image data
	device->ImageRect()->Update(DeviceUpdateMode::UpdateDeviceImage);

	WriteImageData(device->DeviceImage());

	SetIntegerNodeMapValue(deviceNodeMap->Node<IntegerNode>("Width"), oldWidth);
}
2 Likes

Hi @silas
I am trying to update my device after changing few node map parameters in c++. I have opened the device as GenTL rather than Vin driver. So, when I try to update my device image from device handle, the program crashes. Any idea about this ?

Hi @keerthitheja
Could you please provide an example with as less overhead as possible so that the problem can be narrowed down. Also please add information about the line of code in which the error occurs.
Thanks :smile:

Hi @silas,

Here is the complete code. I am trying to read some parameters from a json file and then set those parameters to the device using nodemap.

#include <cvb/device_factory.hpp>
#include <cvb/utilities/system_info.hpp>
#include <cvb/driver/stream.hpp>
#include <cvb/global.hpp>
#include <cvb/driver/composite_stream.hpp>
#include <cvb/genapi/node_map_enumerator.hpp>
#include <cvb/driver/image_rect.hpp>

#include<chrono>
#include <time.h>
#include <stdlib.h>
#include <thread>
#include <memory>

#define ACQUISITION_TIME 10
#define MAC_ID_LINE_SCAN "00-0C-DF-0A-6B-6F"
#define LINE_SCAN_DRIVER_TYPE "FILTER" // "FILTER" or "SOCKET" (change to socket for linux)
#define ENABLE_LS_WRITE_DEVICE_IMAGE 1

using namespace Cvb;
using namespace std::chrono;

bool RGB_STREAM_COMPLETE = false;
bool NIR_STREAM_COMPLETE = false;

void SetIntegerNodeMapValue(Cvb::GenApi::IntegerNodePtr fromNode, int toValue) {
	fromNode->SetValue(toValue);
	std::cout << "Set " << fromNode->Name() << " to value: " << fromNode->Value() << std::endl;
}

void SetFloatNodeMapValue(Cvb::GenApi::FloatNodePtr fromNode, float toValue) {
	fromNode->SetValue(toValue);
	std::cout << "Set " << fromNode->Name() << " to value: " << fromNode->Value() << std::endl;
}

void acquire_RGB_stream(Cvb::CompositeStreamPtr stream, std::string save_path_RGB)
{

    try{
        stream->Start();
    }
    catch(std::exception& error){
        std::cout<<"Cannot start RGB stream : "<<error.what()<<std::endl;
    }
    unsigned int i = 0;
    auto start = high_resolution_clock::now(); 

    Cvb::CompositePtr composite;
    Cvb::WaitStatus waitStatus;
    Cvb::NodeMapEnumerator enumerator;

    while(duration_cast<seconds>(high_resolution_clock::now() - start).count() < ACQUISITION_TIME)
    {
        std::tie(composite, waitStatus, enumerator) = stream->Wait();
        switch (waitStatus)
        {
            default:
                std::cout << "wait status unknown" <<std::endl;
            case Cvb::WaitStatus::Abort:
            case Cvb::WaitStatus::Timeout:
            {
                std::cout << "wait status not ok " << std::endl;
                continue;
            }
            case Cvb::WaitStatus::Ok:
            {
            break;
            }
        }
        auto firstElement = composite->ItemAt(0);
        if (!Cvb::holds_alternative<Cvb::ImagePtr>(firstElement))
        {
            std::cout << "composite does not contain an image at the first element\n";
            continue;
        }
        auto image = Cvb::get<Cvb::ImagePtr>(firstElement);
        //image->Save(save_path_RGB + "Image_" + std::to_string(i)+ ".bmp");
        //std::cout<<"Number of Planes in RGB is : " <<image->PlanesCount()<<std::endl;
        auto linearAccess = image->Plane(0).LinearAccess();
      
        i++;
    }
    RGB_STREAM_COMPLETE = true;

    while (RGB_STREAM_COMPLETE && NIR_STREAM_COMPLETE)
    {
        stream->Stop();
        break;
    }
}

void acquire_NIR_stream(Cvb::CompositeStreamPtr stream, std::string save_path_NIR)
{
    try{
        stream->Start();
        std::cout<<"Started NIR Stream"<<std::endl;


    }
    catch(std::exception& error){
        std::cout<<"Cannot start RGB stream : "<<error.what()<<std::endl;
    }
    unsigned int i = 0;
    auto start = high_resolution_clock::now(); 
    
    Cvb::CompositePtr composite;
    Cvb::WaitStatus waitStatus;
    Cvb::NodeMapEnumerator enumerator;

    while(duration_cast<seconds>(high_resolution_clock::now() - start).count() < ACQUISITION_TIME)
    {
        std::tie(composite, waitStatus, enumerator) = stream->Wait();

        switch (waitStatus)
        {
            default:
                std::cout << "wait status unknown" <<std::endl;
            case Cvb::WaitStatus::Abort:
            case Cvb::WaitStatus::Timeout:
            {
                std::cout << "wait status not ok: " << std::endl;
                continue;
            }
            case Cvb::WaitStatus::Ok:
            {
            break;
            }
        }

        auto firstElement = composite->ItemAt(0);
        //std::cout<<"Getting first element in composite stream of NIR "<< i <<std::endl;
        if (!Cvb::holds_alternative<Cvb::ImagePtr>(firstElement))
        {
            std::cout << "composite does not contain an image at the first element\n";
            continue;
        }
      
        auto image = Cvb::get<Cvb::ImagePtr>(firstElement);
        //image->Save(save_path_NIR + "Image_" + std::to_string(i)+ ".bmp");
        //std::cout<<"Number of Planes in NIR is : " <<image->PlanesCount()<<std::endl;
        auto linearAccess = image->Plane(0).LinearAccess();
        i++;
    }

    NIR_STREAM_COMPLETE = true;
    while (RGB_STREAM_COMPLETE && NIR_STREAM_COMPLETE)
    {
        stream->Stop();
        break;
    }

}

int main(int argc, char* argv[])
{
   
    LineScanConfig line_scan_config;
    line_scan_config = parse_json(json_config_path);

    try
    {
        auto path = Cvb::InstallPath();
        path += CVB_LIT("drivers\\GenICam.vin");
    
        std::cout<<"GeniCam Driver Path : "<<path<<std::endl;
        auto flags = Cvb::DiscoverFlags::IgnoreVins | Cvb::DiscoverFlags::IgnoreGevSD;

        auto discovery_tokens = Cvb::DeviceFactory::Discover(flags);
        std::cout<<"Number of Devices Discovered :"<<discovery_tokens.size()<<std::endl;
        unsigned int device_index =  0;
        for(auto &token : discovery_tokens){    
            //std::cout<<"Discover Token : "<<token.AccessToken()<<std::endl;
            if(token.AccessToken().find(MAC_ID_LINE_SCAN) != std::string::npos )
            {
                if(token.AccessToken().find(LINE_SCAN_DRIVER_TYPE) != std::string::npos)
                {
                    break;
                }
                else
                {
                    continue;
                }
            }
            device_index++;
        }
        std::cout<<"Device Index : "<<device_index<<std::endl;
        
        auto line_scan_device = Cvb::DeviceFactory::Open(discovery_tokens[device_index].AccessToken(), Cvb::AcquisitionStack::GenTL);
        
        auto device_node_map = line_scan_device->NodeMap(CVB_LIT("Device"));
        auto old_width = device_node_map->Node<IntegerNode>("Std::Width"); 
        auto old_height = device_node_map->Node<IntegerNode>("Std::Height"); 
        auto frame_rate = device_node_map->Node<FloatNode>("Std::AcquisitionFrameRate"); 
        auto pixelFormat = device_node_map->Node<EnumerationNode>("Std::PixelFormat");
        auto componentSelector = device_node_map->Node<EnumerationNode>("Std::ComponentSelector");
        auto line_rate = device_node_map->Node<FloatNode>("Std::AcquisitionLineRate");
        
        std::cout<<"Line Rate :" <<line_rate->Value() <<std::endl;
        std::cout<<"Line Rate Minimum : " <<line_rate->Min() <<std::endl;
        std::cout<<"Line Rate Maximum : " <<line_rate->Max() <<std::endl;

        std::cout<<"Old Width : " <<old_width->Value() <<std::endl;
        std::cout<<"Old Height : " <<old_height->Value() <<std::endl;
  
        std::cout<<"Component Selector is : "<<componentSelector->Value()<<std::endl;
        std::cout<<"Pixel Format is : "<<pixelFormat->Value()<<std::endl;
        std::cout<<"The FPS of Camera is : "<<frame_rate->Value()<<std::endl;
        
        #if ENABLE_LS_WRITE_DEVICE_IMAGE
            SetIntegerNodeMapValue(device_node_map->Node<IntegerNode>("Std::Width"), line_scan_config.Parameters.image_width);
            SetIntegerNodeMapValue(device_node_map->Node<IntegerNode>("Std::Height"), line_scan_config.Parameters.image_height);

            //line_scan_device->ImageRect()->Update(DeviceUpdateMode::UpdateDeviceImage);

        #endif

        auto set_width = device_node_map->Node<IntegerNode>("Std::Width"); 
        auto set_height = device_node_map->Node<IntegerNode>("Std::Height"); 
        std::cout<<"Set Width : " <<set_width ->Value() <<std::endl;
        std::cout<<"Set Height : " <<set_height ->Value() <<std::endl;
        

        auto stream_RGB = line_scan_device->Stream<Cvb::CompositeStream>(0);
        auto stream_NIR = line_scan_device->Stream<Cvb::CompositeStream>(1);
        
        std::thread th1(acquire_RGB_stream, stream_RGB, save_directory_path_RGB);
        std::thread th2(acquire_NIR_stream, stream_NIR, save_directory_path_NIR);

        th1.join();
        th2.join();

    }
    catch(const std::exception& error)
    {
        std::cout<<" Camera config : "<<error.what()<<std::endl;

    }

    return 0;
}

Now the error happens at this block of code

 #if ENABLE_LS_WRITE_DEVICE_IMAGE
            SetIntegerNodeMapValue(device_node_map->Node<IntegerNode>("Std::Width"), line_scan_config.Parameters.image_width);
            SetIntegerNodeMapValue(device_node_map->Node<IntegerNode>("Std::Height"), line_scan_config.Parameters.image_height);

            //line_scan_device->ImageRect()->Update(DeviceUpdateMode::UpdateDeviceImage);

        #endif

The error happens exactly in the line line_scan_device->ImageRect()->Update(DeviceUpdateMode::UpdateDeviceImage);
I commented this line because the program was crashing but I see that the device width and height are getting updated without updating the device image. Previously I was using python for another camera and I was able to update the device image. There the camera was opened as vin driver.
Here I am using the 3rd Gen Acquisition Stack. So, I am not sure if there is aother way to do this.

Hi @keerthitheja
please strip down your example to the absolute minimum for us to reproduce the error.
Alternatively you can use the example already provided by @silas here in this thread and simply change the discovery of the device to match what you do in your application.

Cheers
Chris

Hi @Chris
I followed the same example given in this thread. The only difference is, I am opening my camera using GenTL Acquisition Stack and the example shown here does that with Vin Acquisition stack. Now, when I open the device with GenTL, I am able to set the node map parameters but the program crashes when calling the function device->ImageRect()->Update(DeviceUpdateMode::UpdateDeviceImage)

So, I was wondering if the device image has to be updated explicitly in case of GenTL. If so, is there any other way to do it

Ahoi @keerthitheja

for the GenTL Stack you need RegisterManagedFlowSetPool(int count).
This will replace the old “RingBuffer” with a new one of size “count”.

https://help.commonvisionblox.com/API/C++/class_cvb_1_1_driver_1_1_composite_stream_base.html#a42c8dcf66f58afa436cb8a5c3c092fd4

1 Like