CVB skips frames while storing consecutive images

Hello all,
I am not sure if it is right place to ask this question here, but I will post it anyway.

I have a SP5000 CE GigE Camera with which i try to acquire images. I have followed the guidelines given in CVB user guide to set up the ethernet ports.
ens1

You can see from the above image, that the port is configure to maximum MTU 8912 to receive jumbo packets. Also, I have increased the receive descriptors to 4096, the maximum value supported by the hardware. The same can be seen in the below image for the port ens1 where the camera is connected.
receive_descriptors

Now, whenever I try to record or store consecutive images using the genI Cam tool, the logging messages say some frames are getting skipped. The logging messages can be seen in the below image

Also, the logging messages say

“Corrupt frames were delivered due to lost packets. Packet resend is already activated. Please optimize network card settings (e.g. receive descriptors)”

But, you can see from the above that the setting are already configure to the maximum possible values.
Am I missing something or doing wrong ?

Appreciate any help regarding this.

Thanks in advance :slight_smile:

Hi @keerthitheja ,

I would like to highlight the following section in our configurator:

NOTE: When activated in the network card this does not mean that Jumbo Frames are used. This only means that the network card can handle Jumbo Frames.

To really use Jumbo Frames this has to be activated in the camera by setting the packet size to the appropriate value.

The appropriate value depends on the maximum available packet size in the camera and the maximum setting in the network card.

As recommendation use 8192 as PacketSize if supported by camera and network card, it was used very often without problems.

Normally, if Jumbo Frames are disabled, this does not cause problems with lost packets or lost frames.

But as the camera is not able to send the images as fast as they are acquired from the sensor, you can have a visible delay in the live image.

Please make sure, that the packet size is set correctly for the camera as well:
Activate Jumboframes in Camera/Driver

In your screenshot you tried to auto save consecutive images when you received the error.
Does this also happen if you are not trying to auto save?

Cheers
Chris

Hi @Chris,
Thank you for your reply. I have already set the camera packet size to 8912 in the camera configuration. I missed this information in the post but the skipping frames is happening with jumbo frames set from the network card as well as from the camera configuration.

I don’t see this skipping frames without auto saving the consecutive images. It happens only when saving the images

Hi @keerthitheja ,

ok now that we figured that out I am pretty sure, that its not the network settings that are our problem here.
To understand what I think might be the issue I have to go back a bit.

:cvb: uses a buffer when acquiring images. This RingBuffer (consisting of storage to cover 3 of your images in length, width and number of planes) is created at the moment you load the camera in your application.
This is a good thread about the different ways to work with the Ringbuffer

Long story short, there are different ways to access this 3 Buffers but in any case, once your acquisition or framerate respectively, is faster than your processing (in your case saving the images) you will fill up your buffer up to the point you start losing frames.

The reason this happens is, that with the auto save consecutive images you write one single file every time. This has a (depending on the OS) more or less expensive overhead.
This feature is not designed for recording lots of image data in short time.

What you could do is right click on your camera and have a look if you can increase the buffercount or use the wrench/gear icon in the GenICamBrowser.
Warning: I dont know what the GenICam browser does if you stop acquisiton but still have pending buffers and auto save consecutive images enabled.
This would be something you could check… please let me know about the answer :slight_smile:

If the GenICam browser checks for pending images and saves them after stopping an active grab, this would mean you just have to increase the number of buffers to a number that covers the time you want to record. You just have to be sure, that you have enough RAM for doing this.

Honestly, I am afraid this is a feature that is not implemented as it is not what you would typically do.

So there are three options I suggest from here on (assuming the increased amount of buffers dont do the trick either):

  1. Have a look at MovieInteractive2
    This tool saves images into a container, getting rid of the single file operations and boosting performance

  2. Write your own application using the 3rd generation acquisition stack, to have your own image buffer:
    3rd Gen Stack

  3. Check the following snippets using the .vin driver to perform 20 triggers on the camera (leaving you with 20 buffers filled) and processing them afterwards.
    You could do this for freerun as well, just make sure to have enought buffers.
    As I dont know which programming language you prefer I will cover C#, Py and C++ for you here:

C#:

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

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

      Console.WriteLine($"Current RingBuffer amount: {device.Stream.RingBuffer.Count()}"); 
      device.Stream.RingBuffer.ChangeCount(20, DeviceUpdateMode.UpdateDeviceImage); 
      Console.WriteLine($"Changed RingBuffer amount to: {device.Stream.RingBuffer.Count()}");   

      var triggerMode = deviceNodeMap["TriggerMode"] as EnumerationNode; 
      var oldTriggerMode = deviceNodeMap["TriggerMode"] as EnumerationNode; 
      var triggerSource = deviceNodeMap["TriggerSource"] as EnumerationNode; 
      var oldTriggerSource = deviceNodeMap["TriggerSource"] as EnumerationNode; 
      var triggerSoftware = deviceNodeMap["TriggerSoftware"] as CommandNode;   

      triggerMode.Value = "On"; 
      triggerSource.Value = "Software";  

      Console.WriteLine("Starting stream and trigger camera given amount of times"); 
      device.Stream.Start();   

      // collect images to fill buffer 
      while(device.Stream.Statistics[StreamInfo.NumBuffersPending] < 20) 
      { 
        triggerSoftware.Execute(); 
      } 
      Console.WriteLine($"Currently locked buffers: {device.Stream.Statistics[StreamInfo.NumBuffersLocked]}"); 
      Console.WriteLine($"Currently pending buffers: {device.Stream.Statistics[StreamInfo.NumBuffersPending]}");   

      // process through images in buffer  
      while (device.Stream.Statistics[StreamInfo.NumBuffersPending] > 0)  
      { 
        using (var image = device.Stream.Wait()) 
        { 
          // Store your image somewhere
        } 
        Console.WriteLine($"NumBuffersPending: {device.Stream.Statistics[StreamInfo.NumBuffersPending]}"); 
      } 
      device.Stream.Stop();  

      // Reset changed settings of camera 
      device.Stream.RingBuffer.ChangeCount(3, DeviceUpdateMode.UpdateDeviceImage); 
      triggerMode.Value = oldTriggerMode.Value; 
      triggerSource.Value = oldTriggerSource.Value; 
    } 
  } 
} 

Py:

import cvb 
import os 
import cvb.foundation 
import sys   

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

deviceStream = device.stream() 
ringBufferCount = deviceStream.ring_buffer.count 
print("Current RingBuffer amount: " + str(ringBufferCount)) 

deviceStream.ring_buffer.change_count(20, 0) 
ringBufferCount = deviceStream.ring_buffer.count 
print("Changed RingBuffer amount to: " + str(ringBufferCount))   

triggerMode = deviceNodeMap["TriggerMode"] 
oldTriggerMode = deviceNodeMap["TriggerMode"] 
triggerSource = deviceNodeMap["TriggerSource"] 
oldTriggerSource = deviceNodeMap["TriggerSource"] 
triggerSoftware = deviceNodeMap["TriggerSoftware"] 
triggerMode.value = "On" 
triggerSource.value = "Software"  

print("Starting stream and trigger camera given amount of times") 
device.stream().start()  

# Collect images to fill buffer 
while(deviceStream.statistics[cvb.StreamInfo.NumBuffersPending] < 20): 
    triggerSoftware.execute() 
    print("Currently locked buffers: " + str(deviceStream.statistics[cvb.StreamInfo.NumBuffersLocked])) 
    print("Currently locked buffers: " + str(deviceStream.statistics[cvb.StreamInfo.NumBuffersPending]))
  

# Process through images in buffer 
while(deviceStream.statistics[cvb.StreamInfo.NumBuffersPending] > 0): 
    image = device.stream().wait() 
    print("NumBuffersPending: " + str(deviceStream.statistics[cvb.StreamInfo.NumBuffersPending])) 
device.stream().stop()   

# Reset changed settings of camera 
deviceStream.ring_buffer.change_count(3, 0) 
triggerMode = oldTriggerMode 
triggerSource = oldTriggerSource 

C++:

#include <iostream> 
#include <cvb/device_factory.hpp> 
#include <cvb/driver/stream.hpp> 
using namespace std; 
using namespace Cvb;   

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

cout << "Current RingBuffer amount: " << device->Stream()->RingBuffer()->Count() << endl; 
device->Stream()->RingBuffer()->ChangeCount(20, DeviceUpdateMode::UpdateDeviceImage); 
cout << "Changed RingBuffer amount to: " << device->Stream()->RingBuffer()->Count() << endl;  

auto triggerMode = deviceNodeMap->Node<EnumerationNode>("TriggerMode"); 
auto oldTriggerMode = deviceNodeMap->Node<EnumerationNode>("TriggerMode"); 
auto triggerSource = deviceNodeMap->Node<EnumerationNode>("TriggerSource"); 
auto oldTriggerSource = deviceNodeMap->Node<EnumerationNode>("TriggerSource"); 
auto triggerSoftware = deviceNodeMap->Node<CommandNode>("TriggerSoftware"); 
triggerMode->SetValue("On"); 
triggerSource->SetValue("Software");  

cout << "Starting stream and trigger camera given amount of times" << endl; 
device->Stream()->Start();   

/// Collect images to fill buffer 
while (device->Stream()->Statistics(StreamInfo::NumBuffersPending) < 20) { 
triggerSoftware->Execute(); 
}  

cout << "Currently locked buffers: " << device->Stream()->Statistics(StreamInfo::NumBuffersLocked) << endl; 
cout << "Currently pending buffers: " << device->Stream()->Statistics(StreamInfo::NumBuffersPending) << endl;   

// Process through images in buffer  
while (device->Stream()->Statistics(StreamInfo::NumBuffersPending) > 0) {  
device->Stream()->Wait(); 
//Save images here
cout << "NumBuffersPending: " << device->Stream()->Statistics(StreamInfo::NumBuffersPending) << endl; 
}   

device->Stream()->Stop();   

// Reset changed settings of camera 
device->Stream()->RingBuffer()->ChangeCount(3, DeviceUpdateMode::UpdateDeviceImage); 
triggerMode->SetValue(oldTriggerMode->Value()); 
triggerSource->SetValue(oldTriggerSource->Value()); 
} 

I hope this helps you find a solution for your usecase.

Cheers
Chris

1 Like

Hello @Chris ,

Thank you for the answer. So, I tried few things and below are the results.

  1. I tried to increase the buffer count in the GenICam tool in configured device options.
    Although, It is given in CVB user guide that as a rule of thumb the buffer count should be set to our camera FPS (21 in my case), I increased the count to more than that. The maximum buffer I could set is 300. If i go above that the camera stops responding ( could be due to the hardware limitations of the camera). I could still see the skipping frames while saving the images.

  2. I wrote a simple python script to read and save the images based on the python code you posted in your previous reply. It is as follows.

import cvb 
import os 
import cvb.foundation 
import sys   
import time


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

print("Acqistion Mode : ", deviceNodeMap["AcquisitionMode"])
print("Trigger Mode : ", deviceNodeMap["TriggerMode"])
print("Trigger Source : ", deviceNodeMap["TriggerSource"])

triggerSoftware = deviceNodeMap["TriggerSoftware"]

discover = cvb.DeviceFactory.discover_from_root()
camera_info = next((info for info in discover if "GenICam.vin" in info.access_token), None)

if camera_info is None:
	raise RuntimeError("Unable to find GenICam.vin")

with cvb.DeviceFactory.open(
	os.path.join(cvb.install_path() + "drivers", "GenICam.vin"), 
	cvb.AcquisitionStack.Vin) as vin_device: 

	stream = vin_device.stream()

	print(stream.ring_buffer.lock_mode)  

	stream.start()
	start_time = time.time()
	i = 0
	#triggerSoftware.execute()
	try:
		while(time.time() - start_time < 1):
			image, _ = stream.wait()
			#print(image.buffer_index)
			#image.save("/home/foragecampc/image_"+str(i)+'.bmp')
			i += 1
	finally:
                print("Total Images Received : {}".format(image.buffer_index))
		stream.try_stop()
vin_device.close()

In the above code, I did not set the software trigger because I wanted a free run of the camera acquisition. I hope I did it correct.

The ring buffer mode is set to AUTO. So, basically I am reading the images as and when they are available from the camera and saving them. So, I am running the acquisition for a total of 1 sec to see if I am able to capture all the frames. So, the buffer count reads 19 which means I am getting 20 frames and when I try to save them via image.save, the buffer count drops to 17 which means 2 frames are getting skipped here. It could be that the images are coming from the camera but getting over ridden in the ring buffer (since the ring buffer mode is AUTO) while I try to save them.

So, is this the correct way to do this ?

I did not want to use the ringbuffer lock and unlock mode because, I have object moving at very high speed and I need to capture everything in front of the camera. So, Is there a way I can try to save the images to a buffer created on RAM and then save them in the end once the acquisition is stopped from the engine. From the second method (3rd Gen Acquisition Stack) of your previous post I understand it is possible but I would like to know how to do that in python

Thanks

Hi @keerthitheja ,

you could basically use my example 1:1 just get rid the NodeMap access.
Still, you should increase the number of buffers somewhere in your code (in my example I used 20 buffers, from your code I cant see you doing this.

There are a few things to consider:

  1. With a free running camera you can never really tell which images are getting saved.
    Lets assume you are running 20 fps in your camera with 20 buffers for the ringbuffer and try to save the current image you got with stream.wait().
    While trying to save (if not multithreaded) your buffers will still get filled until all of them contain image data.
    With lockmode auto, after 1 second, all new frames will be dropped.
    If you call wait now, you get the oldest image in you ringbuffer and as far as I am aware, it should not be possible to overwrite this buffer as long as the image does not leave its scope or get disposed in any way.
    This does not necessarily apply to mutli threaded applications however.

  2. You cant just acquire images free running, then stop the stream and collect the images afterwards.
    As soon as the stream is stopped, you are not allowed to call wait() anymore.
    That is the reason why I implemented the software trigger in my example. This shows exactly how you would cover a certain period of acquisiton and take care of the images afterwards.

  3. The number of buffers is something you set in your driver. This only affects you computer and has nothing to do with the camera.
    Actually increasing the number of buffers takes significantly more time the more buffers you allocate.
    It might be, that it only looks like something is not working as the initialization of the buffers is still going on.

So, the buffers are allocated directly in you RAM. Make sure to have enough RAM to store the number of images you want to save. Simply going with fps * 2 only works if you have peaks in your processing but most of the time you are done bevore the next image arrived in the buffer.
I am pretty sure saving 1 image to disc takes longer than acquiring a new image, and this for every image. Thus you will end up losing images at some point as long as you buffer is not big enough to cover the exact amount of images you want to store.

As mentioned in 2. this will still leave you with the problem, that you cant get images once you stopped the stream.
Thus the best way for you is to have an array that is big enough to hold the amount of images you want to get and simply go with a Ringbuffer of fps * 2 and store the images you get from Wait() into this array.
Thus you can stop the stream at any time but still have access to the images.
This requires 1. to be as assumed. If this is not the case you will have to go for LockMode.On and manage your buffers manually OR you switch to the 3rd gen acquisition stack and have less headache.

Cheers
Chris

Hi @Chris,
Thank you for the reply. I have already set the buffer size to twice the size of my camera FPS. That was done via the GenICam browser and I save the configuration. So, you cannot see the buffer count in my code. Actually, I have objects moving at a speed of 25 m/sec so, I do not want to miss out any frame while capturing the objects. I will try to save the images from the ringBuffer to an array and store them even after the stream is stopped. I will let you know how it goes.

Thanks for all the help :slight_smile:

1 Like

Hello @Chris,
I tried to store the images from the ring buffer, but I feel I am reading them in a wrong way. I am still unclear about how to read/access the images from ring buffer and store them to the disk.

Say for example, I set my buffer count to 3. Now, when i call stream.wait() it returns a tuple (RingBufferImage, Status). Now, this RingBufferImage class has a property buffer_index. What does this buffer_index point to. I saw the RingBufferClass and it says Gets the index of this buffer in the parent device's ring buffer. I am not able to understand this statement. What is the parent device’s ring buffer ? Like imagine the buffer as a list, so can i read the buffers like RingBufferImage[index] when all the 3 buffers are filled ?

I would appreciate if you point me to some example or tutorials on how to do that.

Thanks in advance

Regards
Keerthitheja

Hi @keerthitheja ,

you dont need to worry about the index of the buffer for now, as you have no use case for doing so.
Also the Ringbuffer always exists in the backround even if you do not actively configure it (unless using the 3rd Gen Acqisition Stack).
It is part of how CVB acquires images and per default reservates storage for three images.

You have two options from here, as you dont want to trigger your camera:

  1. Set the Number of Buffers as high as possible (limited by your RAM) and simply start the stream.
    Now you can call wait() to get the oldes acquired image out of the RingBuffer and can save this.
    Saving will very likely take longer than it takes for the next Buffer to be filled with new image data, thus your RingBuffer will fill up step by step.
    Every wait() call decreases the amount of images in the RingBuffer by 1 (the oldest).

At some point you will also see lost images as the RingBuffer will have image data in all buffers (as saving always takes longer than getting new image data).
This is the point where you want to stop acquiring new images and first take care to save all images in the (now full) RingBuffer.
This is a Problem because as soon as you stop the stream, you cant call wait() anymore meaning as soon as you loose the first image you have to stop the stream and discard all other images from your RingBuffer as well.

As I still dont know what you exact use case ist, this might still be OK for you as you already covered the time you wanted to when the RingBuffer is maxed out.
If this is not OK for you as you also want all the images in the buffer after you saw the first image being lost, then you will need

  1. an Array, List, Vector (I still dont know what language you are programming in).
    Now you can leave your RingBuffer at the default size which means you dont have to make any calls inlcuding the RingBuffer, you simply start your stream and write each image into your container.
    Be careful, to check the RAM if your container does not have a fixed size.
    Now you can call wait() to get your images, put them into your container, from there put them into your save routine. At some point your container will have an image behind all indexes or your RAM will be filled up to a critical level.
    Simply stop the stream now.
    As you have all your images stored in an independent container you can simply take them and save the rest of the images now.

Example:

1:

import cvb 
import os 
import cvb.foundation 
import sys   

device = cvb.DeviceFactory.open(os.path.join(cvb.install_path(), "drivers", "GenICam.vin")) 

deviceStream = device.stream() 
ringBufferCount = deviceStream.ring_buffer.count 
print("Current RingBuffer amount: " + str(ringBufferCount)) 

# Reservate space for 2000 images
deviceStream.ring_buffer.change_count(2000, 0) 
ringBufferCount = deviceStream.ring_buffer.count 
print("Changed RingBuffer amount to: " + str(ringBufferCount))   

device.stream().start()  

# While no images has been lost due to RingBuffer Overflow, collect images from RingBuffer
while(deviceStream.statistics[cvb.StreamInfo.NumBuffersLostLocked] < 1):   
    image = device.stream().wait() 

device.stream().stop()   

2:

import cvb 
import os 
import cvb.foundation 
import sys   

device = cvb.DeviceFactory.open(os.path.join(cvb.install_path(), "drivers", "GenICam.vin")) 
deviceStream = device.stream() 

images = []

device.stream().start()  

# Acquire the desired amount of images OR get images until RAM fills up to a certain level 
#TODO: implement this check
for i in range(2000)   
  images.append(device.stream().wait())
#when working in a while loop that runs until a certain level of RAM is reached or something similar,
#you may want to get multithreaded here to already start saving images.

device.stream().stop()   

# Get all images from the array and save them
while(len(images) > 0):
  images.pop(0).save(os.path.join(cvb.install_path(), "tutorial", "tetetet.bmp"))

The problem with saving single files is, that there is always a lot of overhead from the OS.
Maybe you want to have a look at your CVB installation folder → Applications.
There is a tool called MovieInteractive that takes care of this problem and might be a good solution for you:
CVB Movie

Cheers
Chris

Hello @Chris

Sorry for the previous post. It was a very silly mistake of mine and I figured it out. So, I deleted the post

Regards
Keerthitheja S C

Hello @Chris,

I hope you are doing good. I was initially working with python, but then moved to C++ as I was not able to get the multi-threading working as I needed. A little background of what I am doing currently. So, I have a global queue which take a local queue. This local queue contains the images. So, I have two threads running currently, one thread for acquisition of the data stream and the other thread to write the images to the disk. So, far I tried to run the acquisition for 10 secs at 42 FPS and see if I am able to get all the images and it is happening with C++. But with Python, it is not the case. Here is the code for that.

C++:

#include <iostream>

#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<yaml-cpp/yaml.h>
#include<AreaScanner/area_scan.hpp>
#include<string>
#include <bits/stdc++.h>
#include <sys/stat.h>
#include <sys/types.h>
#include<chrono>
#include <time.h>
#include <queue>
#include <thread>

using namespace std;
using namespace Cvb;
using namespace chrono;

queue <queue <Cvb::StreamImagePtr>> Global_Queue;

bool STOP_STREAM_FLAG = false;

void acquire_images_from_stream(Cvb::V_1_5::Driver::StreamPtr stream)
{
    cout<<"Acquire Thread Started " <<endl;
    stream->Start();
    int i = 0;
    auto start = high_resolution_clock::now(); 
    while (duration_cast<seconds>(high_resolution_clock::now() - start).count() < 10)
    {
      queue <Cvb::StreamImagePtr> Image_Queue;
      while(Image_Queue.size() < 30)
      {
        auto waitResult = stream->WaitFor(std::chrono::seconds(2));
        if (waitResult.Status == Cvb::WaitStatus::Timeout)
        {
          throw std::runtime_error("acquisition timeout");
        }
        Image_Queue.push(image);
      }
      Global_Queue.push(Image_Queue);
    }
    cout<<"Size of Global Queue : " <<Global_Queue.size()<<endl;
    stream->Stop();
    STOP_STREAM_FLAG = true;
}
void write_images_to_disk(string save_directory_path)
{
  cout<<"Write Thread Started : "<<endl;
  int i = 0;
  while(true)
  {
    //cout<<"Size of Global Queue : "<<Global_Queue.size()<<endl;
    if(Global_Queue.size() > 2)
      break;
  }
  Cvb::StreamImagePtr Queue_value;
  //cout<<"Size of Image Queue : " <<Image_Queue.size()<<endl;
  queue <Cvb::StreamImagePtr> Temp_Queue;
  while(!Global_Queue.empty() or STOP_STREAM_FLAG) 
  {
    cout<<"Inside if of Write thread" <<endl;
    Temp_Queue = Global_Queue.front();
    while(!Temp_Queue.empty())
    {
    
      cout<<"Entered while of write thread"<<endl;
      cout<<"Size of Image Queue : " <<Temp_Queue.size()<<endl;

      Queue_value = Temp_Queue.front();
      //cout<<"Address of shared pointers :" << Queue_value <<endl;
      Queue_value->Save(save_directory_path+"Image"+std::to_string(i)+".bmp");
      i++;
      Temp_Queue.pop();
    }
    Global_Queue.pop();   
  } 
  cout<<"Size of Image Queue : " <<Global_Queue.size()<<endl;
}

int main(int argc, char* argv[])
{
  
  string yaml_config_path;
  string save_directory_path; 
  yaml_config_path = argv[1];
  AreaScanConfig area_scan_config;

  area_scan_config = parse_yaml(yaml_config_path);
  save_directory_path = area_scan_config.Paths.save_path ;
  try
  {
    auto path = Cvb::InstallPath();
    path += CVB_LIT("drivers/GenICam.vin");
    
    cout<<"GeniCam Driver Path : "<<path<<endl;

    // open a device
    auto area_scan_device = Cvb::DeviceFactory::Open(path, Cvb::AcquisitionStack::Vin);
    
    cout << "Current RingBuffer amount: " << area_scan_device->Stream()->RingBuffer()->Count() << endl; 
    auto oldBufferCount = area_scan_device->Stream()->RingBuffer()->Count();
    area_scan_device->Stream()->RingBuffer()->ChangeCount(area_scan_config.Parameters.buffer_count, DeviceUpdateMode::UpdateDeviceImage); 
    cout << "Changed RingBuffer amount to: " << area_scan_device->Stream()->RingBuffer()->Count() << endl;  

    auto stream = area_scan_device->Stream();

    std::thread th1(acquire_images_from_stream, stream);
    std::thread th2(write_images_to_disk, save_directory_path);
    th1.join();
    th2.join();
    area_scan_device->Stream()->RingBuffer()->ChangeCount(oldBufferCount, DeviceUpdateMode::UpdateDeviceImage); 
  }  
  catch (const exception& error)
  {
    cout << "Failed to open the device: " << error.what() << endl;
    return 1;
  }

  return 0;

}

  1. Here, Since Waitresult.Image returns a Cvb::V_1_5::Driver::StreamImagePtr, I had to increase the buffer count in device stream and then copy it to a queue. If I don’t increase the buffer size and start writing to the disk with the default buffer size of 3, then I see only last three frame getting saved for all the images. I tried to access the value of the pointer into the queue but it was throwing me deleted function error. I tried to initialize the queue with queue<Cvb::StreamImage>. Is there a way to directly access the Image as a value instead of pointer and put it to the containers ?

  2. Using python, I first tried with multi-threading and then multi-processing.

Python:

import os
import sys
import cvb
import pprint
import argparse
import signal
import time
import cvb.ui
import threading
import queue
import multiprocessing


sys.path.append(os.path.join(os.getcwd(), '../src/utils/'))
import utils  

global_queue = queue.Queue()

def acquire_stream(stream):
    print("Process 1 started !!!)")

    stream.start()
    image_count = 0
    start_time = time.time()
    try:
        while(time.time() - start_time < 10):
            image, status = stream.wait()
            signal.signal(signal.SIGINT, signal_handler)
            if status == cvb.WaitStatus.Ok:
                print("Acquired image " + str(image_count) + " into buffer " + 
                        str(image.buffer_index) + ".")
                global_queue.put(image)
                    
            else:
                raise RuntimeError("timeout during wait"  
                if status == cvb.WaitStatus.Timeout else "acquisition aborted")
            image_count += 1
    except:
        pass 
    finally:
        stream.try_abort()
        print("Size of Queue : ", global_queue.qsize())

def write_stream_to_disk():
    ''' TBD: Write to disk from the queue'''
    print("Process 2 started !!!")
    


def signal_handler(sig, frame):
    print('Exiting the program due to Ctrl+C!')
    sys.exit(0)

parser = argparse.ArgumentParser()
parser.add_argument('-as_cfg', '--area_scan_config',  
                    help="Area Scanner Configuration File Path")

args = parser.parse_args()
as_config = utils.ConfigParse(args.area_scan_config)

area_scan_attributes = as_config.get_yaml_attributes()

save_directory = os.path.join(area_scan_attributes["Paths"]["save_path"], \
                              "Day_"+str(area_scan_attributes["Paths"]["day"]), \
                              "Run_"+str(area_scan_attributes["Paths"]["run"]))

if not os.path.isdir(save_directory):
    os.makedirs(save_directory)

interface_flags = cvb.DiscoverFlags.UpToLevelInterface | cvb.DiscoverFlags.IgnoreGevSD
discover_all_interfaces = cvb.DeviceFactory.discover_from_root(interface_flags)

camera_info = next((info for info in discover_all_interfaces if "GenICam.vin" in info.access_token), None)

if camera_info is None:
    raise RuntimeError("Unable to find GenICam.vin")

print("Found GenICam.vin !!!")
print("Opening the device")
print("\n")
with cvb.DeviceFactory.open(
    os.path.join(cvb.install_path() + "drivers", "GenICam.vin"), 
    cvb.AcquisitionStack.Vin) as vin_device:
    
    init_area_scanner = utils.InitializeAreaScan(vin_device, area_scan_attributes)

    stream = vin_device.stream()

    ringBufferCount = stream.ring_buffer.count 
    print("Current RingBuffer amount: " + str(ringBufferCount)) 

    #stream.ring_buffer.change_count(20, 0) 
    #ringBufferCount = stream.ring_buffer.count 
    #print("Changed RingBuffer amount to: " + str(ringBufferCount)) 
    
    
    if(area_scan_attributes["Flags"]["display_stream"]):
        display_stream(vin_device)
    p1 = multiprocessing.Process(name='p1', target=acquire_stream, args=(stream, ))
    p2 = multiprocessing.Process(name='p2', target=write_stream_to_disk)

    p1.start()
    p2.start()

    print("Done !!!")

Whenever I pass the stream variable as an argument to to the thread or process as in the code above p1 = multiprocessing.Process(name='p1', target=acquire_stream, args=(stream, )) it doesn’t work. The thread exits without starting the stream. But when I pass it as an argument directly to the function like this p1 = multiprocessing.Process(name='p1', target=acquire_stream(stream)), the acquisition happens and the images get written to the container. Here again I see two problems

(I) With the above line, the processes happen one after the other. So, when process 1 (p1) is completed then the p2 starts. I know this is the issue of passing the stream variable as a function argument instead of the reference to the process. How do we start the multi process or multi thread correctly ?

(II) This is the same issue of the buffer count again, with the default buffer count of 3 from device dream, the container takes only the last 3 frames for all the images. Say, my FPS is 42 and I am running it stream for 3 seconds, I get a total of 126 images, all these images are having only the last three frames (I guess this is because the buffer are holding a reference rather than a value). So, how do i go about this without increasing the buffer count or converting the image to numpy array and storing it to the container.

Any suggestions would really be appreciated. Thanks in advance.

Best Regards,
Keerthitheja S C

Hi @keerthitheja,

from what you describe, the LockMode::Auto in the RingBuffer seems not to be the right way when working multithreaded.
I was not 100% sure about this but what you are facing seems to indicate exactly that.

What you can try now is to set the Lockmode to On.

Edit: 3 years ago I had a similar issue and was already asking for the Lockmode back then:
https://forum.commonvisionblox.com/t/saving-images-in-own-thread/937
So this should definately do the trick.

Cheers
Chris