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;
}
-
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 ?
-
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