I am trying to use OpenCV to stream video from 2 cameras continuously via separate threads. The following code is displaying Segmentation fault (core dumped)
What is the reason for this and How do I fix this issue?
main.cpp
#include <iostream>
#include <pthread.h>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/videoio.hpp>
using namespace std;
struct thread_data {
string path;
int thread_id;
};
void *capture(void *threadarg)
{
struct thread_data *data;
data = (struct thread_data *) threadarg;
cv::VideoCapture cap(data->path);
if( !cap.isOpened())
{
std::cout<<"Not good, open camera failed"<<std::endl;
}
std::cout<< "Opened IP camera successfully!"<<std::endl;
cv::Mat frame;
string ext = ".jpg";
string result;
while (true) {
cap >> frame;
cv::imshow("Frame",frame);
cv::waitKey(1);
}
pthread_exit(NULL);
}
int main(void) {
pthread_t threads[2];
struct thread_data td[2];
int rc=0;
for( int i = 0; i < 2; i++ ) {
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[0].path = "rtsp://admin:opencv123#192.168.1.23:554/Streaming/Channels/101/";
td[1].path = "rtsp://admin:opencv123#192.168.1.24:554/Streaming/Channels/101/";
rc = pthread_create(&threads[i], NULL, capture, (void *)&td[i]);
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
return 0;
}
log:
main() : creating thread, 0 main() : creating thread, 1
Segmentation fault (core dumped)
When I tried running it multiple times, I am able to open only one camera and that too isn't streaming continuously. It starts and stops in a few seconds.
Sometimes I get an error which says
OpenCV Error: Insufficient memory (Failed to allocate 140703464366800 bytes) in OutOfMemoryError
I have gone through various Q&As' on StackOverflow but none helped.
The problem here is that the code is facing race conditions. I was able to replicate the issue on my system and identified the following issues:
OpenCV window titles are not unique.
Spawned threads are not joined.
Race condition while opening the video stream.
Lets look into these issues in detail.
1.
OpenCV windows are uniquely identified by their title. In the current code, the title is a hardcoded string "Frame". So basically, both threads are creating/updating/destroying the same window in unknown order. That is a race condition which can be fixed by adding a string field to the struct thread_data which will serve as unique window identifier.
2.
In the main thread, the child threads are created asynchronously so the for loop will exit immediately after creating the threads and the program will exit pre-maturely without waiting for the spawned thread to complete execution. This problem can be solved by adding function calls to wait for the threads before the program exits. This process is called joining and can be achieved by calling pthread_join for each spawned thread.
3.
This issue was a bit trickier to track down. Due to some reason the backend library for video stream capture used by OpenCV is not behaving in a thread-safe manner. Seemingly, the video capture opening process is prone to race-condition and requires a synchronization lock. The lock can be easily implemented by calling the functions pthread_mutex_lock and pthread_mutex_unlock just before and after opening the VideoCapture object.
Here is the modified code demonstrating the solution for all of the above mentioned issues
#include <iostream>
#include <pthread.h>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/videoio.hpp>
using namespace std;
//Mutex for thread synchronization
static pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;
struct thread_data
{
string path;
int thread_id;
string window_title; //Unique window title for each thread
};
void *capture(void *threadarg)
{
struct thread_data *data;
data = (struct thread_data *) threadarg;
cv::VideoCapture cap;
//Safely open video stream
pthread_mutex_lock(&foo_mutex);
cap.open(data->path);
pthread_mutex_unlock(&foo_mutex);
if( !cap.isOpened())
{
std::cout<<"Not good, open camera failed"<<std::endl;
}
std::cout<< "Opened IP camera successfully!"<<std::endl;
cv::Mat frame;
string ext = ".jpg";
string result;
//Create window with unique title
cv::namedWindow(data->window_title);
while (true)
{
cap >> frame;
cv::imshow(data->window_title,frame);
cv::waitKey(10);
}
//Release VideoCapture object
cap.release();
//Destroy previously created window
cv::destroyWindow(data->window_title);
//Exit thread
pthread_exit(NULL);
}
int main(void)
{
const int thread_count = 2;
pthread_t threads[thread_count];
struct thread_data td[thread_count];
//Initialize thread data beforehand
td[0].path = "rtsp://admin:opencv123#192.168.1.23:554/Streaming/Channels/101/";
td[0].window_title = "First Window";
td[1].path = "rtsp://admin:opencv123#192.168.1.24:554/Streaming/Channels/101/";
td[1].window_title = "Second Window";
int rc=0;
for( int i = 0; i < thread_count; i++ )
{
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
rc = pthread_create(&(threads[i]), NULL, capture, (void *)& (td[i]) );
if (rc)
{
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
//Wait for the previously spawned threads to complete execution
for( int i = 0; i < thread_count; i++ )
pthread_join(threads[i], NULL);
pthread_exit(NULL);
return 0;
}
void writeFrametoDisk(const cv::Mat *frame, std::string path, int frameNum, std::string windowName)
{
cv::imwrite(path.append(std::to_string(frameNum)).append(".png"), *frame);
return;
}
void openCameraStream(int deviceId, std::string dirName)
{
cv::VideoCapture cap;
cap.open(deviceId);
if(!cap.isOpened()){std::cerr << "Unable to open the camera " << std::endl;}
std::cout << cap.get(cv::CAP_PROP_FRAME_WIDTH) << " " << cap.get(cv::CAP_PROP_FRAME_HEIGHT) << std::endl;
std::string windowName = deviceId == 0 ? "Cam 0" : "Cam 1";
std::string outputDir = dirName;
bool frameStFg = false;
while(!frameStFg)
{
cv::Mat frame;
cap.read(frame);
if(frame.empty()){std::cerr << "frame buffer empty " << std::endl;}
else
{
frameStFg = true;
}
}
cv::TickMeter timer;
timer.start();
int frameCount = 0;
while(1)
{
cv::Mat frame;
cap.read(frame);
if(frame.empty()){std::cerr << "frame buffer empty " << std::endl;}
frameCount++;
std::thread th(writeFrametoDisk, &frame, outputDir, frameCount, windowName);
th.join();
//// a simple wayto exit the loop
if(frameCount > 500)
{
break;
}
}
timer.stop();
std::cout << "Device id " << deviceId << " Capture ran for " << timer.getTimeSec() << " seconds" << std::endl;
std::cout << "Device id " << deviceId << " Number of frames to be capture should be " << timer.getTimeSec() * 30 << std::endl;
std::cout << "Device id " << deviceId << " Number of frames captured " << frameCount << std::endl;
cap.release();
}
int main(int argc, char * argv[])
{
std::string outputDir1 = "";
std::string outputDir2 = "";
std::thread camera1Thread(openCameraStream, 0, outputDir1);
std::thread camera2Thread(openCameraStream, 1, outputDir2);
camera1Thread.join();
camera2Thread.join();
}
As the comments mention, streaming without any imshow() of the image streams works well with out any problems. My setup includes, running two threads with two USB camera's and also these two threads launch a new thread each to save the image read from the cameras. I didn't observe any frame drops or errors and also was able to capture and write at approximately 30 fps.
A little debugging of the reason, it is recommended to use any imshow() in the main thread only i.e. main() function. Hope this helps anyone.
Related
I want a thread to run infinity times in order to execute the task described on do_work() function that it receives. However, the function is only called on the pthread_create() subroutine.
I've tried to implement the sched_yield() and the pthread_join() routines on a while loop. But it didn't work yet.
Is there any routine in which I can call the existing thread again?
int main (int argc, char ** argv) {
int period;
int priority;
int load;
char schedule[15];
period = atoi(argv[1]);
priority = atoi(argv[2]);
load = atoi(argv[3]);
strncpy(schedule,argv[4],100);
std::cout << " period : " << period <<"\n priority : "<< priority << "\n load : "<< load << "\n schedule : " << schedule <<std::endl;
struct sched_param param;
pthread_t thread;
int rc;
sched_setscheduler (0, SCHED_FIFO , ¶m);
std::cout << "main() : creating thread " << std::endl;
rc = pthread_create(&thread, NULL, do_work, (void*)load);
if (rc) {
std::cout << "Error:unable to create thread " << rc << std::endl;
exit(-1);
}
int i=0;
struct sigaction action;
struct itimerval timer;
while(i<10000){
pthread_join(thread, NULL);
sched_yield();
i++;
}
pthread_exit(NULL);
}
You do not call a thread, you create a thread. By doing that, you specify a start_routine which will be called 'in' the new thread.
If you want to call repeatedly a function in a loop, then you can do the following in your start_routine:
void* start_routine(void *arg) {
while (active) { // active: atomic global boolean value
do_work();
}
// if no longer active,
// there could be an option to wait to become active again,
// or exit the thread
pthread_exit(NULL);
}
pthread_join() is only called, if you want to join a thread with other thread(s). pthread_join() waits until the target thread has terminated. By joining the thread, all resources are given back to the system (cleanup).
Just wanted to show you the code I implemented.
I was not sure about the meaning of Threads when I made this question and you helped me to understand that I cannot access the function on a thread multiple times, but I have to create it on each use.
My main objective was to find a way of calling the do_work() function on the reception of the signal SIGALRM. Thus, I just assumed the do_wordk() to be my Thread and used a sigaction struct to control the arrival of the signal.
If you guys want to test the code, it returns the execution time of the do_work() function and a message if the deadline set on your period was lost. The purpose of this work was to make an analogy with periodic threads.
To compile:
g++ teste.cpp -o exe -lrt
To run:
sudo taskset -c 0 ./exe 300 1 100000 F
sudo taskset -c 0 ./exe Period Priority Work_Load Scheduller_Policy
#include<signal.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/time.h>
#include<iostream>
#include<string>
#include<string.h>
long load=1;
void deadline();
void do_work();
void wakeup(int j){
struct itimerval aux;
int t1, t2;
getitimer( ITIMER_REAL, &aux); //Get initial resume time
t1 = aux.it_value.tv_usec;
//std::cout << "Hello World! Thread working |Resume Time : " <<t1<< std::endl;
do_work();
getitimer( ITIMER_REAL, &aux);
t2 = aux.it_value.tv_usec; //Get Final resume time
std::cout << "Execution time (usec): " <<t1 - t2<< std::endl;
if (t2==0){
deadline();
}
return;
}
void do_work(){
for ( int i = 0; i < load * 1000; i++) {
/* do nothing , keep counting */
}
}
void deadline() {
std::cout << "Lost deadline!" << std::endl;
}
int main (int argc, char ** argv) {
int i;
int period;
int priority;
char scheduler[5];
period = atoi(argv[1])*1000;
priority = atoi(argv[2]);
load = atoi(argv[3]);
strcpy(scheduler, argv[4]);
std::cout << " period : " << period <<"\n priority : "<< priority << "\n load : "<< load << "\n scheduler : " << scheduler <<std::endl;
struct sched_param param;
param.sched_priority = priority;
if (scheduler[0]=='F'){
int r = sched_setscheduler (0, SCHED_FIFO , ¶m);
if(r==-1){ perror("scheduller"); return 1;}
std::cout <<"FIFO scheduller: "<<r<<std::endl;
}else{
int r = sched_setscheduler (0, SCHED_RR , ¶m);
if(r==-1){ perror("scheduller"); return 1;}
std::cout <<"RR scheduller: "<<r<<std::endl;
}
struct itimerval val;
struct sigaction action;
sigset_t mask;
sigemptyset(&action.sa_mask);
action.sa_handler = wakeup;
action.sa_flags=SA_RESTART;
if(sigaction(SIGALRM, &action, 0)==-1){
perror("sigaction");
return 1;
}
val.it_interval.tv_sec=0;
val.it_interval.tv_usec=period;
val.it_value.tv_sec=0;
val.it_value.tv_usec=period;
if(setitimer(ITIMER_REAL, &val, 0)==-1){
perror("setitimer");
return 1;
}
if(sigwait( &mask, &i)==-1){
perror("sigwait");
}
return 0;
}
Finally, I am really grateful for your patience in understanding my problem. This is my first question on this community and I hope I'll improve them over time. Thank you all for your answers and the effort on helping me.
I have a code here that captures the video stream from a camera running the video capture operation in a separate thread. The code compiles but when I try to run it I am receiving the following error.
I did some research and found out that this problem is related to the absence of th.join(); but I already do this when the user interrupts the process by pressing the q key. Until then, the code should keep on running in the created thread. So this tells me that I should join the thread somewhere else.
If I remove the thread by replacing thread th(&VideoCaptureAsync::update, this); with VideoCaptureAsync::update(); and deleting th.join();, the error goes away but this obviously defeats the purpose of this code.
Output
Inside initiate function
(process:10748): GStreamer-CRITICAL **: gst_element_get_state:
assertion 'GST_IS_ELEMENT (element)' failed
Inside start function
Inside update function
terminate called without an active exception
Inside read function
Aborted (core dumped)
Code
/*
* Asynchronous_video_capture.cpp
*
* Copyright (C) 2019 C. S. G.
*
* MIT License
*/
#include <iostream> // for standard I/O
#include <string> // for strings
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp> // Basic OpenCV structures (cv::Mat)
#include <opencv2/videoio.hpp> // Video write
#include <opencv2/opencv.hpp>
#include <opencv2/core/utility.hpp>
#include <thread>
#include <tuple>
using namespace cv;
using namespace std;
void foo(bool &keep_run){
cout << "Inside the thread: Interrupt flag = " << keep_run << endl;
if (std::cin.get() == 'q') {
keep_run = false;
cout << "User Interrupted the process. Interrupt flag = " << keep_run << endl;
}
}
class VideoCaptureAsync
{
private:
VideoCapture cam;
thread th;
bool read_lock;
Mat frame;
Mat grabbed;
bool isStarted;
public:
void initiate(unsigned int camId, unsigned int width, unsigned int height, double fps);
void start(); // a start function to create and start the thread
void update(); // an update function that will be called asynchronously
tuple<Mat, Mat> read(); // a read function that we will call from our code to retrieve a new frame.
void stop(); // a stop function to stop (join) the thread
void exit(); // an __exit__ function to clean up some resources.
};
void VideoCaptureAsync::initiate(unsigned int camId, unsigned int width, unsigned int height, double fps){
cout << "Inside initiate function" << endl;
cam.open(camId);
if (!cam.isOpened())
{
cerr << "Could not open the VideoCapture camera: " << camId << endl;
}
cam.set(CV_CAP_PROP_FRAME_WIDTH, width);
cam.set(CV_CAP_PROP_FRAME_HEIGHT, height);
cam.set(CAP_PROP_FPS, fps);
isStarted = false;
read_lock = false;
VideoCaptureAsync::start();
}
void VideoCaptureAsync::start(){
cout << "Inside start function" << endl;
if (isStarted) {
cout << "Asynchroneous video capturing has already been started" << endl;
}
isStarted = true;
thread th(&VideoCaptureAsync::update, this);
//VideoCaptureAsync::update();
}
void VideoCaptureAsync::update(){
cout << "Inside update function" << endl;
while(isStarted){
Mat frame_update;
Mat grabbed_update;
tie(frame_update, grabbed_update) = VideoCaptureAsync::read();
if(!read_lock){
frame_update.copyTo(frame);
grabbed_update.copyTo(grabbed);
}
}
}
tuple<Mat, Mat> VideoCaptureAsync::read(){
cout << "Inside read function" << endl;
if (!read_lock){
read_lock = true;
Mat frame_read;
cam.read(frame_read);
Mat grabbed_read;
read_lock = false;
return make_tuple(frame_read, grabbed_read);
}
}
void VideoCaptureAsync::stop(){
cout << "Inside stop function" << endl;
th.join();
isStarted = false;
read_lock = true;
}
void VideoCaptureAsync::exit(){
cout << "Finished writing ..." << endl;
cam.release();
}
int main(int argc, char *argv[]){
const unsigned int camId = 1;
const bool enableOutput = true;
const unsigned int w = 1280;
const unsigned int h = 720;
double fps = 30.0;
VideoCaptureAsync obj;
obj.initiate(camId,w,h,fps);
bool keep_running = true;
thread th1(foo, std::ref(keep_running));
Mat original_frame;
while (keep_running) {
std::tie(std::ignore, original_frame) = obj.read();
if (enableOutput) {
imshow("Retrieved Image", original_frame);
waitKey(1000/fps);
}
}
obj.stop();
obj.exit();
}
In your start() method:
thread th(&VideoCaptureAsync::update, this);
This statement creates a new local object in the start() method called th, a brand new, fresh off the assembly line, std::thread object, and constructs it in a manner that creates a new execution thread.
Immediately afterwards, start() returns. This destroys this local object, invoking its destructor, and resulting in your exception.
Your obvious intent is to use the th class member in order to create a new execution thread, instead of creating a new local object in the start() method. But the above C++ syntax is a declaration. It declares a new object, and when used in side a function it creates a new local object (in automatic scope), that gets automatically destroyed at the end of the scope. That's how C++ works.
In order to have the new execution thread started from your existing class member:
th=thread{&VideoCaptureAsync::update, this};
(using modern C++'s uniform initialization syntax). For more information see "Delayed start of thread in C++11".
I have the following reduced program that spins up a bunch of child processes and then uses a boost::interprocess::message_queue to send a message to each one. This works when number of processes is small (about 4 on my machine) but as that number rises I get the following message:
head (81473): "./a.out"
Assertion failed: (res == 0), function do_wait, file /usr/local/include/boost/interprocess/sync/posix/condition.hpp, line 175.
I'm guessing it's a problem with my synchronisation.. have I done something wrong or is the boost::interprocess::scoped_lock not enough?
My program is here:
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/process.hpp>
#include <iostream>
auto main(int argc, char **argv) -> int
{
namespace ip = boost::interprocess;
boost::filesystem::path self{argv[0]};
if (argc == 1) {
std::cout << "head (" << ::getpid() << "): " << self << std::endl;
// create a message queue.
ip::message_queue::remove("work_queue");
ip::message_queue tasks{ip::create_only, "work_queue", 100, sizeof(int)};
// mutex for writing to the queue.
ip::interprocess_mutex mutex{};
// spawn off a bunch of processes.
const auto cores{5 * std::thread::hardware_concurrency()};
std::vector<boost::process::child> workers{};
for (auto i = 0; i < cores; ++i) {
workers.emplace_back(self, "child");
}
// send message to each core.
for (auto i = 0; i < cores; ++i) {
ip::scoped_lock<decltype(mutex)> lock{mutex};
tasks.send(&i, sizeof(i), 0);
}
// wait for each process to finish.
for (auto &worker : workers) {
worker.wait();
}
} else if (argc == 2 && std::strcmp(argv[1], "child") == 0) {
// connect to message queue.
ip::message_queue tasks{ip::open_only, "work_queue"};
// mutex for reading from the queue.
ip::interprocess_mutex mutex{};
unsigned int priority;
ip::message_queue::size_type recvd_size;
{
ip::scoped_lock<decltype(mutex)> lock{mutex};
int number;
tasks.receive(&number, sizeof(number), recvd_size, priority);
std::cout << "child (" << ::getpid() << "): " << self << ", received: " << number << std::endl;
}
}
return 0;
}
You create an interprocess_mutex instance on the stack. So each process has it's own mutex and locking it does not synchronize anything. You need to create a shared memory region, place mutex there and then open the same shared memory region in child process to access the mutex created by parent process.
I am creating a program to practice using threads. I am trying to name them so that when the program is run, you can clearly see "Flight 1 is taking off..." or "Flight 6 is landing..." and so on. I would like every thread to have a flyTime (so I know what order they will use the runway in) which will be randomly generated. I have tried and am having difficulty using struct/typedef to give each pthread these characteristics so i can say for example flight.flyTime and use it throughout the program. Here is the relevant part of my code without my landing/takeoff functions:
#include <pthread.h>
#include <stdio.h>
#include <cstdlib>
#include <iostream>
#include <queue>
#define NUM_THREADS 8 //8 flights
pthread_mutex_t runway1lock;
void *FlightID(void *flightid){
long fid;
fid = (long)flightid;
pthread_exit(NULL);
}
typedef struct{ //each plane has these characteristics
long fid;
int StartState; // if start=1 ==> taking off:::if start=2 ==> landing
int flyTime; //fly == randomly generated time (order)
}FLIGHTS;
FLIGHTS flights[NUM_THREADS];
int StartState(flights[NUM_THREADS]){
int startState;
for (int i=0; i<=NUM_THREADS; i++){
startState = rand() % 1+2;
}
std::string start;
if(startState == 1){
start = "Taking off";
}
if(startState == 2){
start = "Landing";
}
for (int t=0; t<NUM_THREADS; t++){
std::cout << "Start State for Flight# " << FlightID << " is " << start << std::endl;
}
return startState;
}
int main(int argc, char *argv[]){
// pthread_t flights[NUM_THREADS]; //pthread_t keeps a thread ID after the thread is created with pthread_create()
//it's like an index on a vector of threads
int rc;
long t;
for (t=1; t<=NUM_THREADS; t++){ //loop creates threads(flights)
printf("In main: Creating flight %1d\n", t);
rc = pthread_create(&flights[t], NULL, FlightID, (void *)t);
if (rc){
printf("ERROR: return code from pthread_create() is %d\n", rc);
return (-1);
}
printf("Created flight %1d\n", t);
StartState(flights[t]); //gives every flight a start state
if(StartState(flights[t])==1){
std::cout << "Flight # " << &flights[t] << " is listed as waiting at the gate." << std::endl;
//go to takeoff function and go through switch case
}
if(StartState(flights[t])==2){`enter code here`
std::cout << "Flight # " << &flights[t] << " is listed as waiting to land." << std::endl;
//go to landing function and go through switch case
}
}
pthread_exit(NULL);
}
There is a code snippet below that represents how I would implement it.
You should also take a look at pthread_key_create, pthread_getspecific and pthread_setspecific. This is a set of functions that allow you to have data, specific to each thread, stored in the thread's memory context. It may come in handy for in your code, later on.
typedef struct{
long fid;
int StartState;
int flyTime;
} FLIGHTS;
FLIGHTS** flights = new FLIGHTS*[NUM_THREADS];
pthread_key_t pkey:
void *FlightID(void *flightid){
long fid;
fid = (long)flightid;
FLIGHTS* flight = new FLIGHTS();
flight->fid = fid;
flights[fid] = flight;
pthread_setspecific(pkey, flight);
int startState;
for (int i=0; i<=NUM_THREADS; i++){
startState = rand() % 1+2;
}
std::string start;
if(startState == 1){
start = "Taking off";
}
if(startState == 2){
start = "Landing";
}
for (int t=0; t<NUM_THREADS; t++){
std::cout << "Start State for Flight# " << fid << " is " << start << std::endl;
}
flight->StartState = startState;
}
int main(int argc, char* argv[]) {
pthread_key_create(&pkey, NULL);
for (t=1; t<=NUM_THREADS; t++){
rc = pthread_create(&flights[t], NULL, FlightID, (void *)t);
if (rc){
printf("ERROR: return code from pthread_create() is %d\n", rc);
return (-1);
}
printf("Created flight %1d\n", t);
}
}
Also, I don't know if I'm understanding your code correctly or if you just have some coding errors on it, so I leave you with some questions or remarks that could be mistakes/bugs:
1) You invoke pthread_exit in the start callback function:
void *FlightID(void *flightid){
long fid;
fid = (long)flightid;
pthread_exit(NULL);
}
2) You pass to the << operator a function with no return value:
std::cout << "Start State for Flight# " << FlightID << " is " << start << std::endl;
3) You invoke the same function 3 times just to get the return value. Shouldn't it be int state = StartState(flights[i]) and then test the state variable value?
StartState(flights[t]); //gives every flight a start state
if(StartState(flights[t])==1){
std::cout << "Flight # " << &flights[t] << " is listed as waiting at the gate." << std::endl;
//go to takeoff function and go through switch case
}
if(StartState(flights[t])==2){`enter code here`
std::cout << "Flight # " << &flights[t] << " is listed as waiting to land." << std::endl;
//go to landing function and go through switch case
}
4) You can't define a function like this:
int StartState(flights[NUM_THREADS]){
I am trying to write a program that is able to capture images from two different cameras in two different threads. I want to do this because when I do this in the same thread I have to keep waiting for cvQueryFrame twice the amount of time and so i can not grab images at 30 fps (I get 15 FPS from each camera).
I have taken a look at this SO post, but this only works for one camera. Using cvQueryFrame and boost::thread together
My current program gives varying results, sometimes it gives memory leaks, usually I just don't see anything happening and sometimes it worls for a few seconds but the the image freezes again. The strange thing is that earlier when I didn't call cvShowImage, but had my imageProcessing function do something useful I could see that I was getting real time results from both cameras. I assume this means that it is possible to make this work, but that I made a stupid mistake somewhere. My OS is LINUX and I am using OpenCV 2.4
My code:
#include <iostream>
#include <cstdio>
#include <cv.h>
#include <ml.h>
#include <cvaux.h>
#include <highgui.h>
#include <vector>
#include <stdio.h>
#include "producer_consumer_queue.hpp"
//Camera settings
int cameraWidth = 1280;
int cameraHeight = 720;
int waitKeyValue = 5;
bool threads_should_exit = false;
CvCapture * capture;
CvCapture * capture2;
using namespace std;
using namespace cv;
void grabFrame(concurrent_queue<IplImage* > * frame_queue, int camNumber) {
try {
//Load first frames
cout << "grabFrame: " << camNumber << " init with " << cameraWidth << " x " << cameraHeight << endl;
IplImage* frame;
if (camNumber == 0)frame = cvQueryFrame(capture);
if (camNumber == 1)frame = cvQueryFrame(capture2);
while (frame && !threads_should_exit) {
if (camNumber == 0)frame = cvQueryFrame(capture);
if (camNumber == 1)frame = cvQueryFrame(capture2);
IplImage* frame_copy = NULL;
frame_copy = cvCloneImage(frame);
if (camNumber == 0)cvShowImage("NE", frame);
cout << "grabFrame: " << camNumber << " pushing back to queue" << endl;
frame_queue->push(frame_copy);
int k = cvWaitKey(waitKeyValue);
if (k == 1048603 || k == 27 || k == '\r') {
cout << "grabFrame: Process killed" << endl;
//release memory
threads_should_exit = true;
}
}
} catch (const concurrent_queue<IplImage* >::Canceled & e) {
cout << "grabFrame: Show thread is canceled" << endl;
return;
}
}
void processFrames(concurrent_queue<IplImage* > * frame_queue0, concurrent_queue<IplImage* > * frame_queue1) {
try {
do {
cout << "processFrames: Processing two frames" << endl;
IplImage* frm = NULL;
frame_queue0->wait_and_pop(frm);
IplImage * frm2 = NULL;
frame_queue1->wait_and_pop(frm2);
cvReleaseImage(&frm);
cvReleaseImage(&frm2);
} while (!threads_should_exit);
} catch (const concurrent_queue<IplImage* >::Canceled & e) {
cout << "processFrames: Processing thread is canceled" << endl;
return;
}
}
int main() {
capture = cvCreateCameraCapture(0);
capture2 = cvCreateCameraCapture(1);
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, cameraWidth);
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, cameraHeight);
cvSetCaptureProperty(capture2, CV_CAP_PROP_FRAME_WIDTH, cameraWidth);
cvSetCaptureProperty(capture2, CV_CAP_PROP_FRAME_HEIGHT, cameraHeight);
boost::thread_group frame_workers;
boost::thread_group frame_workers2;
concurrent_queue<IplImage* > frame_queue(&frame_workers);
concurrent_queue<IplImage* > frame_queue2(&frame_workers2);
boost::thread * query_thread = new boost::thread(processFrames, &frame_queue, &frame_queue2);
boost::thread * cam0_thread = new boost::thread(grabFrame, &frame_queue, 0);
usleep(10000);
boost::thread * cam1_thread = new boost::thread(grabFrame, &frame_queue2, 1);
frame_workers.add_thread(query_thread);
frame_workers.add_thread(cam0_thread);
frame_workers2.add_thread(query_thread);
frame_workers2.add_thread(cam1_thread);
while (true) {
if (threads_should_exit) {
cout << "Main: threads should be killed" << endl;
while (!frame_queue.empty()) {
usleep(10000);
}
frame_workers.remove_thread(query_thread);
frame_workers2.remove_thread(query_thread);
frame_workers.remove_thread(cam0_thread);
frame_workers2.remove_thread(cam1_thread);
frame_workers.join_all();
break;
}
usleep(10000);
}
return 0;
}
EDIT:
I added a simple function to detect a piece of paper to see if everything is working fine when I don't call on cvShowImage(). My program can detect a piece of paper fine if I don't call cvShowImage(). If I do the program has strange behavior again and freezes etc.
There should only be one thread manipulating the GUI (this is true for just about any GUI framework). You should organize your code so that cvShowImage is only invoked from the main "GUI thread".
It seems that the work that's being done in query_thread could just as easily be done inside the main thread.