Save function take more execution time while multi threading

Hi, I have written a acquisition program to acquire and save images to disk. The save function is usually taking 80 msecs to save a .bmp image at 2k resolution to the disk. I am running my camera at 22 FPS. So, I always need to buffer few images to the RAM before saving everything to the disk. I am currently running the acquisition and saving functionalities in two different threads. To reduce the buffers on the RAM, I tried to include another thread to write the images. When doing so, the execution time of the save function became 2 times i.e., 160 msecs. This defeats the whole purpose of me running a different thread .

Is the Save function a blocking call ? Like can I call the function simultaneously in two different threads or Will the function wait in a thread to be executed before being called in another thread ?

Any better way to do this ?

Thanks and Regards
Keerthitheja S C

Hi @keerthitheja,

by saving single images to disk, you are limited to the Operating System and the disk how fast it can open, write and close a file. To speed this up, you only can try to use a faster disk like a SSD if you’re not already using one.

If you need to write a limited amount of images check if you have enough RAM and raise the amount of buffers in the CVB driver configuration so that you are able to buffer the needed images.

For long term recording you need to switch to a video stream which will be much faster. This would be possible over our CVB Movie Tool which can write a video Stream into an AVI Container.

Hi @Sebastian,
Indeed I am using a SSD to save the images. Currently I have 32 GB of RAM in my PC. I am running my camera (area scanner) at 22 FPS for a overall recording of 120 seconds per run. So, this means I will be recording 2640 images for a single run. The writing speed to the SSD disk is 80 msecs which approximates to 13 images per second. So, for a total time of 120 seconds, I will be able to save 1560 images. I have set the buffer count to 1100 in the CVB driver to accomodate the remaining images. This takes up almost 15 GB of the RAM

Now the issue is, I have another camera which is a line scanner (dual stream RGB + NIR) also running for 120 seconds at the same time. The images are being recorded at 4k resolution. Here, if I try to increase the buffer count, there will be a over head on the RAM. So, I am trying to increase the writing speed of the images to reduce the buffers in RAM.

A video stream is not an option to my case, as we will acquire the image frames whenever there is an external trigger. The trigger triggers the capture of each frame.

Is it possible to do a video stream with the trigger ? If so, can we do it with CVB++ ?

Okay. I figured out the issue. The Save function is a blocking call. The function in another thread waits till the save function in the previous thread finishes writing.

Hi @keerthitheja

so if you save your images in a single thread, the write time is still 80 millisec?

That seems quite low. (Even if we consider conversion to an RGB format).

13 frames per second saved * 2000 pixel width * 1000 pixel height * 3 bytes per pixel ~= 80 MB/s

Am i getting this right?

Hi @c.hartmann ,
Yes the write time to the disc is 80 millisecs. The writing speed is pretty slow. Actually the calculation is as follows
2560 width x 2048 height x 3 channels @ 13 frames per sec ~= 205 MB/ sec. The writing speed of my SSD is 550 MB/sec. So, it is pretty strange to me that its almost half the speed of my SSD.

This is a very good result writing single images. If you want to write images of another camera, you should consider upgrading your system with a M.2 NVME SSD which is much faster or use another SSD for the second camera.

Writing a Video using a Trigger with CVB Movie would also be possible with CVB++.
The functions can be found in the Namespace CVB::Movie2. Be aware that for using CVB Movie you need an CVB Image Manager + CVB Movie2 licence. This can be tested with a 30-day trial licence.

Hi @Sebastian / @keerthitheja

i looked into the code of saving .bmp.
IF your image format does not allow saving the image in a block it will be save line wise, which might be slower.
I.e. it must be RGB8 or MONO8, width*byte per pixel % 4 must be 0 and some other constraints.

So what does this mean for us.

I would suggest the following:

  1. Test saving as .jpg and .tiff, they might be faster
  2. Test directly dumping the image data (i.e. as a raw buffer). Via Linear Access.

Ah, i got curious and tested it myself. Here are the results: https://forum.commonvisionblox.com/t/image-saving-time-considerations/1717

Hi @c.hartmann

Thank you for the clear explanation. I am little confused about the save function and the pixel format.
This may be a stupid question but if you see the below lines of code, the pixel format of my camera is 8 bit Bayer GR and Pixel Coding is Mono. I am saving my images in .bmp format and not .raw format. So, when I acquire the images from waitResult.Image and save it using Save function, does the images get saved after debayering from BayerGR8 to RGB. or Do I still have to do the debayering once the images are stored. Also, I noticed that the number of planes from the image is 3. So, do these planes correspond to R,G,and B planes or are they something else.

#include<iostream>
#include<chrono>
#include <time.h>

#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>

using namespace Cvb;
using namespace std::chrono;

int main(int argc, char *argv [])
{
try
    {
        auto path = Cvb::InstallPath();
        path += CVB_LIT("drivers\\GenICam.vin");
    
        std::cout<<"GeniCam Driver Path : "<<path<<std::endl;

        auto area_scan_device = Cvb::DeviceFactory::Open(path, Cvb::AcquisitionStack::Vin);
        auto device_node_map = area_scan_device->NodeMap(CVB_LIT("Device")); 

        auto pixelFormat = device_node_map->Node<EnumerationNode>("Std::PixelFormat");
        auto pixelCoding = device_node_map->Node<EnumerationNode>("Std::PixelCoding");
        
        std::cout<<"Pixel Format is : "<<pixelFormat->Value()<<std::endl;
        std::cout<<"Pixel Coding is : "<<pixelCoding->Value()<<std::endl;
        
        auto stream = area_scan_device->Stream();
        stream->Start();
        auto start = high_resolution_clock::now();
        unsigned int i = 0;
        while (duration_cast<seconds>(high_resolution_clock::now() - start).count() < 10)
        { 
            auto waitResult = stream->WaitFor(std::chrono::seconds(2));
            if (waitResult.Status == Cvb::WaitStatus::Timeout)
            {
                 throw std::runtime_error("acquisition timeout");
            } 
            auto image = waitResult.Image;
            std::cout<<" Number of Planes in the image : " << image->PlanesCount() <<std::endl;
            image->Save(save_path+"Image"+std::to_string(i)+".bmp");
            i++;
        }
}
catch (const std::exception& error)
{
        std::cout << "Failed to open the device: " << error.what() << std::endl;
        return 1;
} 
    return 0; 
}

Hi @Sebastian,
Thank you for the reply. We brought the cameras from Stemmer and we do have the licenses for CVB Image Manager and Movie2. We will try with the CVB::Movie2 and see if that solves our problem.

1 Like

Your image is automatically debayered. I.e. the image you are working on is RGB8, and if you save it, this debayered image data is used.

(If you (ever) want to disable automatic conversion then you have to use Cvb::Driver::DiscoveryInformation::SetParameter(…) before opening a camera)

Yes, exactly in that order.