Opencv how to read webcam stream via on GPU? - c++

I can use VideoReader class of OpenCV to decode an IP camera stream or any video file by using its path. This decoding process is using GPU as expected, no problem up to now. Here is simple code which is working fine and using GPU for decoding:
int main()
{
const std::string fname("rtsp://user:pwd#192.168.1.108");
// const std::string fname("/path/to/video/file.mp4"); // this also works
cv::cuda::GpuMat d_frame;
cv::Ptr<cv::cudacodec::VideoReader> d_reader = cv::cudacodec::createVideoReader(fname);
Mat frame;
for (;;)
{
if (!d_reader->nextFrame(d_frame))
break;
Mat myMat(d_frame);
cv::imshow("GPU", myMat);
if (cv::waitKey(3) > 0)
break;
}
return 0;
}
I want to use GPU to capture streams from my webcam as like VideoCapture(0). I know as #berak mentioned here : There is no way to do that with VideoCapture
My questions are:
1 - Is it possible to stream by using GPU with VideoReader class? Because VideoReader class only accepts strings not indexes.
2- What are the other ways to be able to stream by using GPU?

1) Yes, it seems so! I found the following code in the openCV GPU samples here. You could give it a try. You need to have OpenCV built with OpenGL though... Currently that's where I'm stuck.
2) I'm not sure about other options, but here is the code from the Github.
#include <iostream>
#include "opencv2/opencv_modules.hpp"
#if defined(HAVE_OPENCV_CUDACODEC)
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <opencv2/core.hpp>
#include <opencv2/core/opengl.hpp>
#include <opencv2/cudacodec.hpp>
#include <opencv2/highgui.hpp>
int main(int argc, const char* argv[])
{
std::cout << "Starting,...\n";
const std::string fname = "0";
cv::namedWindow("CPU", cv::WINDOW_NORMAL);
cv::namedWindow("GPU", cv::WINDOW_OPENGL);
cv::cuda::setGlDevice();
cv::Mat frame;
cv::VideoCapture reader(fname);
cv::cuda::GpuMat d_frame;
cv::Ptr<cv::cudacodec::VideoReader> d_reader = cv::cudacodec::createVideoReader(fname);
cv::TickMeter tm;
std::vector<double> cpu_times;
std::vector<double> gpu_times;
int gpu_frame_count=0, cpu_frame_count=0;
for (;;)
{
tm.reset(); tm.start();
if (!reader.read(frame))
break;
tm.stop();
cpu_times.push_back(tm.getTimeMilli());
cpu_frame_count++;
cv::imshow("CPU", frame);
if (cv::waitKey(3) > 0)
break;
}
for (;;)
{
tm.reset(); tm.start();
if (!d_reader->nextFrame(d_frame))
break;
tm.stop();
gpu_times.push_back(tm.getTimeMilli());
gpu_frame_count++;
cv::imshow("GPU", d_frame);
if (cv::waitKey(3) > 0)
break;
}
if (!cpu_times.empty() && !gpu_times.empty())
{
std::cout << std::endl << "Results:" << std::endl;
std::sort(cpu_times.begin(), cpu_times.end());
std::sort(gpu_times.begin(), gpu_times.end());
double cpu_avg = std::accumulate(cpu_times.begin(), cpu_times.end(), 0.0) / cpu_times.size();
double gpu_avg = std::accumulate(gpu_times.begin(), gpu_times.end(), 0.0) / gpu_times.size();
std::cout << "CPU : Avg : " << cpu_avg << " ms FPS : " << 1000.0 / cpu_avg << " Frames " << cpu_frame_count << std::endl;
std::cout << "GPU : Avg : " << gpu_avg << " ms FPS : " << 1000.0 / gpu_avg << " Frames " << gpu_frame_count << std::endl;
}
return 0;
}
#else
int main()
{
std::cout << "OpenCV was built without CUDA Video decoding support\n" << std::endl;
return 0;
}
#endif

Related

Google Cloud Vision API Object Detection Model Gives Bus Error on Raspberry Pi

I trained a simple object detection model on Google Cloud with the vision API. After exporting as a tflite model, I tried to run it on a Raspberry Pi 3B+ with the simple starter code below and tensorflow lite 2.6.0rc-2. The code runs the standard MobileNet model fine, but gives a bus error while allocating the tensors with my custom model. I then tried running the same code with my model on WSL debian, which worked. The vision API says it supports ARM edge devices, so I don't understand why it's not working. Does the raspberry pi just not have enough memory? If so, why does it run the more complex MobileNet model?
Test code modified from https://github.com/Qengineering/TensorFlow_Lite_SSD_RPi_32-bits
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/highgui.hpp>
#include <fstream>
#include <iostream>
#include <opencv2/core/ocl.hpp>
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/kernels/register.h"
#include "tensorflow/lite/string_util.h"
#include "tensorflow/lite/model.h"
#include <cmath>
using namespace cv;
using namespace std;
const size_t width = 192;
const size_t height = 192;
std::vector<std::string> Labels;
std::unique_ptr<tflite::Interpreter> interpreter;
static bool getFileContent(std::string fileName)
{
// Open the File
std::ifstream in(fileName.c_str());
// Check if object is valid
if(!in.is_open()) return false;
std::string str;
// Read the next line from File untill it reaches the end.
while (std::getline(in, str))
{
// Line contains string of length > 0 then save it in vector
if(str.size()>0) Labels.push_back(str);
}
// Close The File
in.close();
return true;
}
void detect_from_video(Mat &src)
{
Mat image;
int cam_width =src.cols;
int cam_height=src.rows;
// copy image to input as input tensor
cv::resize(src, image, Size(width,height));
memcpy(interpreter->typed_input_tensor<uchar>(0), image.data, image.total() * image.elemSize());
interpreter->SetAllowFp16PrecisionForFp32(true);
interpreter->SetNumThreads(4); //quad core
// cout << "tensors size: " << interpreter->tensors_size() << "\n";
// cout << "nodes size: " << interpreter->nodes_size() << "\n";
// cout << "inputs: " << interpreter->inputs().size() << "\n";
// cout << "input(0) name: " << interpreter->GetInputName(0) << "\n";
// cout << "outputs: " << interpreter->outputs().size() << "\n";
interpreter->Invoke(); // run your model
const float* detection_locations = interpreter->tensor(interpreter->outputs()[0])->data.f;
const float* detection_classes=interpreter->tensor(interpreter->outputs()[1])->data.f;
const float* detection_scores = interpreter->tensor(interpreter->outputs()[2])->data.f;
const int num_detections = *interpreter->tensor(interpreter->outputs()[3])->data.f;
//there are ALWAYS 10 detections no matter how many objects are detectable
//cout << "number of detections: " << num_detections << "\n";
const float confidence_threshold = 0.5;
for(int i = 0; i < num_detections; i++){
if(detection_scores[i] > confidence_threshold){
int det_index = (int)detection_classes[i]+1;
float y1=detection_locations[4*i ]*cam_height;
float x1=detection_locations[4*i+1]*cam_width;
float y2=detection_locations[4*i+2]*cam_height;
float x2=detection_locations[4*i+3]*cam_width;
Rect rec((int)x1, (int)y1, (int)(x2 - x1), (int)(y2 - y1));
rectangle(src,rec, Scalar(0, 0, 255), 1, 8, 0);
putText(src, format("%s", Labels[det_index].c_str()), Point(x1, y1-5) ,FONT_HERSHEY_SIMPLEX,0.5, Scalar(0, 0, 255), 1, 8, 0);
}
}
}
int main(int argc,char ** argv)
{
float f;
float FPS[16];
int i;
int Fcnt=0;
Mat frame;
chrono::steady_clock::time_point Tbegin, Tend;
for(i=0;i<16;i++) FPS[i]=0.0;
// Load model
std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile("detect.tflite");
// Build the interpreter
tflite::ops::builtin::BuiltinOpResolver resolver;
tflite::InterpreterBuilder(*model.get(), resolver)(&interpreter);
interpreter->AllocateTensors();
// Get the names
bool result = getFileContent("labels.txt");
if(!result)
{
cout << "loading labels failed";
exit(-1);
}
// VideoCapture cap("James.mp4");
VideoCapture cap(0);
if (!cap.isOpened()) {
cerr << "ERROR: Unable to open the camera" << endl;
return 0;
}
cout << "Start grabbing, press ESC on Live window to terminate" << endl;
while(1){
// frame=imread("Traffic.jpg"); //need to refresh frame before dnn class detection
cap >> frame;
if (frame.empty()) {
cerr << "End of movie" << endl;
break;
}
detect_from_video(frame);
Tend = chrono::steady_clock::now();
//calculate frame rate
f = chrono::duration_cast <chrono::milliseconds> (Tend - Tbegin).count();
Tbegin = chrono::steady_clock::now();
FPS[((Fcnt++)&0x0F)]=1000.0/f;
for(f=0.0, i=0;i<16;i++){ f+=FPS[i]; }
putText(frame, format("FPS %0.2f",f/16),Point(10,20),FONT_HERSHEY_SIMPLEX,0.6, Scalar(0, 0, 255));
//show output
imshow("RPi 4 - 2.0 GHz - 2 Mb RAM", frame);
char esc = waitKey(5);
if(esc == 27) break;
}
cout << "Closing the camera" << endl;
// When everything done, release the video capture and write object
cap.release();
destroyAllWindows();
cout << "Bye!" << endl;
return 0;
}
Stack trace of bus error, occurs during tensor allocation
Program terminated with signal SIGBUS, Bus error.
#0 0x00134dd0 in tflite::ops::builtin::broadcastto::ResizeOutputTensor(TfLiteContext*, tflite::ops::builtin::broadcastto::BroadcastToContext*) ()
(gdb) bt
#0 0x00134dd0 in tflite::ops::builtin::broadcastto::ResizeOutputTensor(TfLiteContext*, tflite::ops::builtin::broadcastto::BroadcastToContext*) ()
#1 0x00135194 in tflite::ops::builtin::broadcastto::Prepare(TfLiteContext*, TfLiteNode*) ()
#2 0x000d36c4 in tflite::Subgraph::PrepareOpsStartingAt(int, std::vector<int, std::allocator<int> > const&, int*) ()
#3 0x000d386c in tflite::Subgraph::PrepareOpsAndTensors() ()
#4 0x000d5c64 in tflite::Subgraph::AllocateTensors() ()
#5 0x0001b2cc in tflite::Interpreter::AllocateTensors() ()
#6 0x000161d8 in main(int, char**) (argc=1, argv=0x7ebd0644)
at MobileNetV1.cpp:106
Tflite object detection model trained for a single label type with 50 images (I want to get the model to work before I add more)
https://storage.googleapis.com/ml-data-storage-bucket/models/model-export/iod/tflite-ping_pong_ball_1-2021-08-02T19%3A46%3A09.324008Z/model.tflite

Is it possible to crop raw video before extracting all the frames using OpenCV and C++?

I want to crop a certain area of my raw video before extracting the frames. I don't want to use external cropping video software because I need the video to be raw for processing. Is it possible to crop a certain area before extraction ? I read about ROI for images, is it possible for video too ?
Here's my extraction code that's already working :
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <string>
#include <sstream>
using namespace cv;
using namespace std;
int c = 0;
string int2str(int &);
int main(int argc, char **argv) {
string s;
VideoCapture cap("test_video.mov");
if (!cap.isOpened())
{
cout << "Cannot open the video file" << endl;
return -1;
}
double fps = cap.get(CV_CAP_PROP_FPS);
cout << "Frame per seconds : " << fps << endl;
namedWindow("MyVideo", CV_WINDOW_NORMAL);
resizeWindow("MyVideo", 600, 600);
while (1)
{
Mat frame;
Mat Gray_frame;
bool bSuccess = cap.read(frame);
if (!bSuccess)
{
cout << "Cannot read the frame from video file" << endl;
break;
}
s = int2str(c);
//cout<<("%d\n",frame )<<endl;
c++;
imshow("MyVideo", frame);
imwrite("frame" + s + ".jpg", frame);
if (waitKey(30) == 27)
{
cout << "esc key is pressed by user" << endl;
break;
}
}
return 0;
}
string int2str(int &i) {
string s;
stringstream ss(s);
ss << i;
return ss.str();
}
Still need to crop the video, any advice ?

How to use caffemodel with OpenCV on iOS?

I am trying to use a .caffemodel alongside OpenCV on iOS devices. I found this github repository, but it can only be built with Xcode 6. I am working with Xcode 7, but I also downloaded Xcode 6 and still have no success on building it.
How can I use a caffemodel with OpenCV on iOS 9?
PS: The alternative would be this but it's written with swift & metal and I need to be able to use it with OpenCV.
You can use OpenCV DNN contrib module.
You need first to build OpenCV with contrib modules, you can find the steps here.
Then you can import and use the .caffemodel following this tutorial.
Here is an updated version of the tutorial, since it's not working as is:
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace cv::dnn;
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;
/* Find best class for the blob (i. e. class with maximal probability) */
void getMaxClass(dnn::Blob &probBlob, int *classId, double *classProb)
{
Mat probMat = probBlob.matRefConst().reshape(1, 1); //reshape the blob to 1x1000 matrix
Point classNumber;
minMaxLoc(probMat, NULL, classProb, NULL, &classNumber);
*classId = classNumber.x;
}
std::vector<String> readClassNames(const char *filename = "synset_words.txt")
{
std::vector<String> classNames;
std::ifstream fp(filename);
if (!fp.is_open())
{
std::cerr << "File with classes labels not found: " << filename << std::endl;
exit(-1);
}
std::string name;
while (!fp.eof())
{
std::getline(fp, name);
if (name.length())
classNames.push_back( name.substr(name.find(' ')+1) );
}
fp.close();
return classNames;
}
int main(int argc, char **argv)
{
cv::dnn::initModule();
String modelTxt = "bvlc_googlenet.prototxt";
String modelBin = "bvlc_googlenet.caffemodel";
String imageFile = (argc > 1) ? argv[1] : "space_shuttle.jpg";
Ptr<dnn::Importer> importer;
try //Try to import Caffe GoogleNet model
{
importer = dnn::createCaffeImporter(modelTxt, modelBin);
}
catch (const cv::Exception &err) //Importer can throw errors, we will catch them
{
std::cerr << err.msg << std::endl;
}
if (!importer)
{
std::cerr << "Can't load network by using the following files: " << std::endl;
std::cerr << "prototxt: " << modelTxt << std::endl;
std::cerr << "caffemodel: " << modelBin << std::endl;
std::cerr << "bvlc_googlenet.caffemodel can be downloaded here:" << std::endl;
std::cerr << "http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel" << std::endl;
exit(-1);
}
dnn::Net net;
importer->populateNet(net);
importer.release(); //We don't need importer anymore
Mat img = imread(imageFile);
if (img.empty())
{
std::cerr << "Can't read image from the file: " << imageFile << std::endl;
exit(-1);
}
resize(img, img, Size(224, 224)); //GoogLeNet accepts only 224x224 RGB-images
dnn::Blob inputBlob = dnn::Blob(img); //Convert Mat to dnn::Blob batch of images
net.setBlob(".data", inputBlob); //set the network input
net.forward(); //compute output
dnn::Blob prob = net.getBlob("prob"); //gather output of "prob" layer
int classId;
double classProb;
getMaxClass(prob, &classId, &classProb);//find the best class
std::vector<String> classNames = readClassNames();
std::cout << "Best class: #" << classId << " '" << classNames.at(classId) << "'" << std::endl;
std::cout << "Probability: " << classProb * 100 << "%" << std::endl;
return 0;
} //main
I will post another answer because with recent versions there are some differences.
First of all now dnn is already inside the standard OpenCV library so you do not have to build it from contrib_modules.
The function to load the network is readNetFromCaffe.
For example the following code load the NN:
std::string modelName = "path/to/mymodel.caffemodel";
std::string protoName = "path/to/deploy.prototxt";
cv::dnn::Net net;
try
{
net = cv::dnn::readNetFromCaffe(protoName, modelName);
}
catch (cv::Exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
if (net.empty())
{
std::cerr << "Can't load network by using the following files: " << std::endl;
std::cerr << "prototxt: " << protoName << std::endl;
std::cerr << "caffemodel: " << modelName << std::endl;
std::cerr << "bvlc_googlenet.caffemodel can be downloaded here:" << std::endl;
std::cerr << "http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel" << std::endl;
exit(-1);
}
}
Then you can run the NN:
cv::Mat res_mat;
float res;
cv::Mat inputBlob = cv::dnn::blobFromImage(roi, 1.0f, cv::Size(227, 227),
cv::Scalar(0, 0, 0), false);
net.setInput(inputBlob);
//During the forward pass output of each network layer is computed,
//but in this example we need output from "prob" layer only.
res_mat = net.forward("score");
std::cout<<res_mat<<std::endl;
res_mat = res_mat.reshape(1, 1); //reshape the blob to 1x2 matrix
return res_mat.at<float>(0);
The function cv::dnn::blobFromImage resize the image to the input network size specified by the third argument (in my case cv::Size(227, 227)). The argument cv::Scalar(0, 0, 0) is to subtract the means from the three BGR channels.
score is the name of the output layer in the NN I used. You can see this information in the prototxt file.

Can't access properties of cv::VideoCapture with Logitech C920

I am developing a small webcam application with Logitech C920 and OpenCV.
I can actually get images from camera without any problem with given resolution. But except for frame width and height, I can't access any setting in the camera. I have following code. As you can guess, the code produced all zero lines except for the first two.
Am I doing something wrong? or Is this another proprietary BS from Logitech? If this is limitation of OpenCV, is there any other option to develop an application for this webcam in Windows OS?
m_cam.open(0);
if(!m_cam.isOpened() ) // check if we succeeded
{
std::cerr << "ERROR: Could not open cameras." << std::endl;
return;
}
int ex = static_cast<int>(m_cam.get(CV_CAP_PROP_FOURCC));
char EXT[] = {ex & 0XFF , (ex & 0XFF00) >> 8,(ex & 0XFF0000) >> 16,(ex & 0XFF000000) >> 24, 0};
m_cam.set(CV_CAP_PROP_FOURCC,CV_FOURCC('H','2','6','4'));
m_cam.set(CV_CAP_PROP_FRAME_WIDTH,1280);//2304);//1829//1200//800
m_cam.set(CV_CAP_PROP_FRAME_HEIGHT,720);//1536); //1080//800//600
m_cam.set(CV_CAP_PROP_FPS, 30);
//m_cam.set(CV_CAP_PROP_EXPOSURE,0);
std::cout<< m_cam.get(CV_CAP_PROP_FRAME_WIDTH) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_FRAME_HEIGHT) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_FPS) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_EXPOSURE) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_FORMAT) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_CONTRAST) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_BRIGHTNESS) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_SATURATION) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_HUE) << std::endl;
std::cout<< m_cam.get(CV_CAP_PROP_POS_FRAMES) << std::endl;
Fixed the problem by rebuilding OpenCV after getting dshow and ffmpeg installed. I can even set some of the values such as frame rate now, but the camera working as specified seems to be a separate matter. In my case, after setting resolution without setting frame rate, camera resolution goes to 640 x 480. Although my computer has H264 decoder installed, 1920 x 1080 produces 5-7 fps with OpenCV.
In this post is an example code how you can access your camera at 30fps in full hd.
Edit:
To elaborate a bit:
I also have the Logitech c920, OpenCV 2.4.3, Windows 7 64bi. This are the things i can read with the code below (width and height is by default on 640*480 i think).
CV_CAP_PROP_FRAME_WIDTH 1920
CV_CAP_PROP_FRAME_HEIGHT 1080
CV_CAP_PROP_FPS 0
CV_CAP_PROP_EXPOSURE -5
CV_CAP_PROP_FORMAT -1
CV_CAP_PROP_CONTRAST 128
CV_CAP_PROP_BRIGHTNESS 128
CV_CAP_PROP_SATURATION 128
CV_CAP_PROP_HUE -8.58993e+008
CV_CAP_PROP_POS_FRAMES -1
CV_CAP_PROP_FOURCC -4.66163e+008
Input codec type: }Ù6õ //Obviously wrong
The Code i used:
#include <iostream> // for standard I/O
#include <string> // for strings
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat)
#include <opencv2/highgui/highgui.hpp> // Video write
#include "opencv2/opencv.hpp"
using namespace cv; using namespace std;
void getCameraInfo(VideoCapture m_cam){
std::cout<<"CV_CAP_PROP_FRAME_WIDTH " << m_cam.get(CV_CAP_PROP_FRAME_WIDTH) << std::endl;
std::cout<<"CV_CAP_PROP_FRAME_HEIGHT " << m_cam.get(CV_CAP_PROP_FRAME_HEIGHT) << std::endl;
std::cout<<"CV_CAP_PROP_FPS " << m_cam.get(CV_CAP_PROP_FPS) << std::endl;
std::cout<<"CV_CAP_PROP_EXPOSURE " << m_cam.get(CV_CAP_PROP_EXPOSURE) << std::endl;
std::cout<<"CV_CAP_PROP_FORMAT " << m_cam.get(CV_CAP_PROP_FORMAT) << std::endl; //deafult CV_8UC3?!
std::cout<<"CV_CAP_PROP_CONTRAST " << m_cam.get(CV_CAP_PROP_CONTRAST) << std::endl;
std::cout<<"CV_CAP_PROP_BRIGHTNESS "<< m_cam.get(CV_CAP_PROP_BRIGHTNESS) << std::endl;
std::cout<<"CV_CAP_PROP_SATURATION "<< m_cam.get(CV_CAP_PROP_SATURATION) << std::endl;
std::cout<<"CV_CAP_PROP_HUE "<< m_cam.get(CV_CAP_PROP_HUE) << std::endl;
std::cout<<"CV_CAP_PROP_POS_FRAMES "<< m_cam.get(CV_CAP_PROP_POS_FRAMES) << std::endl;
std::cout<<"CV_CAP_PROP_FOURCC "<< m_cam.get(CV_CAP_PROP_FOURCC) << std::endl;
int ex = static_cast<int>(m_cam.get(CV_CAP_PROP_FOURCC)); // Get Codec Type- Int form
char EXT[] = {(char)(ex & 255) , (char)((ex & 0XFF00) >> 8),(char)((ex & 0XFF0000) >> 16),(char)((ex & 0XFF000000) >> 24), 0};
cout << "Input codec type: " << EXT << endl;
}
int main(int, char**){
string resVideoPath = "C:\\yourPath\\video.avi";
VideoCapture vidSource;
double fps=10;
vidSource = VideoCapture(0); // open the default camera
vidSource.set(CV_CAP_PROP_FRAME_WIDTH, 1920);
vidSource.set(CV_CAP_PROP_FRAME_HEIGHT, 1080);
if(!vidSource.isOpened()){
cout << "Could not open the input video to read"<< endl;
return -1;
}
getCameraInfo(vidSource);
namedWindow("Capture", CV_WINDOW_AUTOSIZE);
while(true){
Mat frame;
vidSource >> frame;
if(!frame.data){
cerr << "Could not retrieve frame.";
return -1;}
imshow("Capture", frame);
if(waitKey(1) == 27)
break;
}
return 0;
}

Using OpenCV, Boost threading and multiple cameras

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.