OpenCV - Capture frames in a separatethread - c++

I'm trying to use OpenCV 2.4.8 to capture frames in my android application. I write a .SO library for it and link OpenCV libraries to it.
From this link: http://docs.opencv.org/modules/core/doc/intro.html, I wrote a sample code as below.
int main() {
VideoCapture cap(0);
if(!cap.isOpened()) return -1;
capture(cap);
}
void capture(VideoCapture cap) {
for(int i = 0; i<= 3; i++)
{
cap >> frame;
}
}
This code works just fine as long as I run it from main thread. If I try to run it in a separate thread e.g.
std::thread t(capture, cap);
It can't capture the frames. It gives me some fatal error at the code line
cap>> frame;
in the above code. However, in the above OpenCV documentation, it clearly says "The current OpenCV implementation is fully re-enterable. That is, the same function, the same constant method of a class instance, or the same non-constant method of different class instances can be called from different threads. Also, the same cv::Mat can be used in different threads because the reference-counting operations use the architecture-specific atomic instructions."
Any idea what I'm missing here?
EDIT:
I tried opening camera within the thread and modified capture() as below:
void capture() {
VideoCapture cap(0); // fails here
if(!cap.isOpened()) return -1;
for(int i=0; i <= 3; i++) {
cap >> frame;
}
}
It fails while opening the camera itself which is quite strange. Any suggestions?

As far as I know the thread cant return results, so you need to pass cap by reference or it is better if you use future, using future with threads help you return results from a function.
Instead of using threads use async which is a better fit with threads.

Can you try the following:
#include <iostream>
#include <thread>
using namespace cv;
void capture(VideoCapture &cap)
{
Mat frame;
for(int i = 0; i<= 3; i++)
{
cap >> frame;
// do something with frame e.g. imshow("image", frame);
}
}
int main(int argc, char *argv[])
{
VideoCapture cap(0);
if(!cap.isOpened()) return -1;
std::thread t(capture, std::ref(cap));
// in the meantime do something else here
t.join(); // wait for the thread to be finalized
return 0;
}
Here I'm passing the cv::VideoCapture object by reference. It should solve your problem.

Related

Visual Studio 2010 OpenCV Multithread Mat does not copy to another Mat

Good afternoon to all!
I've been using Visual Studio 2010 with OpenCV to develop a code for Face recognition. I'm trying to reach the task by two Threads (I need to do it this way, because i´m going to apply it on a bigger project), one (the main) to show the frames and the second to capture (from the Webcam of my laptop) and store the frames on a Mat object (Fast Capture).
The inconvenient here is that the second Thread is capturing the frames, but the main is not showing them. I think there is a problem with copying the Mat from the capture Thread to the Mat on the main thread ("current_frame" seems to be empty after I do the assignation)
Here is the code (I'm using Boost::Thread for Multithreading)
New code with suggestions
Global declarations
#include <iostream>
#include <stdio.h>
#include <boost\thread.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
boost::mutex mtx;
The function
void camCapture(VideoCapture& cap, Mat& frame, bool* Capture)
{
while (*Capture==true)
{
mtx.lock();
cap >> frame;
mtx.unlock();
if(frame.empty())
{
cout<<"No hay captura"<<endl;
}
else
{
cout<<"Frame capturado"<<endl;
}
}cout << "camCapture finished\n";
return;}
The main
int main() {
try{
VideoCapture cap(0); // open the default camera
Mat frame,current_frame, SFI, Input;
bool *Capture = new bool;
*Capture = true;
if (!cap.isOpened()) // check if we succeeded
return -1;
//your capture thread has started
boost::thread captureThread(camCapture, cap, frame, Capture);
while(1)
{
if(frame.empty())
{
cout<<"Frame en hilo principal vacio"<<endl;
}
else{
cout<<"Frame en hilo principal capturado"<<endl;
}
mtx.lock();
current_frame = frame.clone();
mtx.unlock();
if(current_frame.empty())
{
cout<<"Current_Frame vacio"<<endl;
}
else{
imshow("Streaming",current_frame);
if(waitKey(10)==27)break;
}
}
//Terminate the thread
captureThread.join();
}
catch(Exception & e)
{
cout<<e.what()<<endl;
}
return 0;}
according to Boost threads - passing parameters by reference you have to use boost::ref(v) if you want to pass a variable by reference to a boost::thread function. But you could use pointers instead.
In addition you should share the mutex to the thread by passing it as pointer or reference variable instead of using it as global:
#include <iostream>
#include <stdio.h>
#include <boost/thread.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
boost::mutex mtx;
void camCapture(VideoCapture& cap, Mat& frame, bool* Capture)
{
while (*Capture == true)
{
mtx.lock();
cap >> frame;
mtx.unlock();
if (frame.empty())
{
cout << "No hay captura" << endl;
}
else
{
cout << "Frame capturado" << endl;
}
}cout << "camCapture finished\n";
return;
}
int main() {
try{
VideoCapture cap(0); // open the default camera
Mat frame, current_frame, SFI, Input;
bool *Capture = new bool; // better not use a pointer here, but use a bool and pass the address or by reference.
*Capture = true;
if (!cap.isOpened()) // check if we succeeded
return -1;
//your capture thread has started
boost::thread captureThread(camCapture, boost::ref(cap), boost::ref(frame), Capture);
while (1)
{
mtx.lock();
current_frame = frame.clone();
mtx.unlock();
if (current_frame.empty())
{
cout << "Current_Frame vacio" << endl;
}
else{
imshow("Streaming", current_frame);
if (waitKey(10) == 27)
{
// TODO: you should use a mutex (or an atomic type) here, too, maybe setting a bool is thread-safe, but this can't be guaranteed for each hardware!
*Capture = false;
break;
}
}
}
//Terminate the thread
captureThread.join();
// give memory free:
delete Capture;
}
catch (Exception & e)
{
cout << e.what() << endl;
}
return 0;
}

Visual Studio Error: name must be a namespace name

This is the 3rd question for today as I am still struggling. The problem is: Visual Studio is giving the error on line 2. It says:
Error: name must be a namespace name
This is my code, this should open the webcam and it should take frames.
What is wrong here?
#include "opencv.hpp"
using namespace cv;
int main(int argc, char** argv)
{
VideoCapture cap;
// open the default camera, use something different from 0 otherwise;
// Check VideoCapture documentation.
if (!cap.open(0))
return 0;
for (;;)
{
Mat frame;
cap >> frame;
if (frame.empty()) break; // end of video stream
imshow("this is you, smile! :)", frame);
if (waitKey(1) == 27) break; // stop capturing by pressing ESC
}
// the camera will be closed automatically upon exit
// cap.close();
return 0;
}

OpenCV camera capture from within a thread

This is a small part of the code I'm trying to get to work. This is also one of my first times working with C++. I'm used to higher-level languages, like Java or C#.
The main version is meant to be run as a shared object or DLL. The idea is that an external program (in C#) will start the main loops. The frames from the camera will be captured in a thread. Information will processed inside of that thread and copied to an array ("dataArray"). This copy process will be done while a class mutex is locked. Then, another function called externally will copy that saved array ("dataArray") to a second array ("outArray") and return a pointer to the second array. The external program will use the pointer to copy the data from the second Array, which will not be modified until the function is called again.
But for all that to work, I need the frames to constantly be captured. I realized that I needed something to keep my main function going, so I'm keeping an infinite loop in there. In the "real" version, the keepRunning variable will be changed by the external program running the library.
I was recently lectured on StackOverflow about not making global variables, so I'm keeping the one instance of my class in a static member. That's pretty standard in Java. I don't know if it's bad practice in C++. I was also taken by surprise as to how C++ threads start as soon as they're created, without an explicit "start" instructions. That's why I'm putting my only thread in a vector. That seems to be what most people recommend.
I understand that without keepRunning never being actually changed, the threads will never be joined, but I'll dear with that later. I'm running this on a Mac, but I'll need it to eventually run on Windows, Mac and Linux.
Here's my header:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>
using namespace cv;
using namespace std;
class MyCap {
public:
MyCap();
VideoCapture cap;
static MyCap * instance;
void run();
static void RunThreads(MyCap * cap);
bool keepRunning = true; // Will be changed by the external program.
vector<thread> capThreads;
private:
Mat frame;
};
And here's my code:
#include "theheader.h"
MyCap * MyCap::instance = NULL;
int main(int argc, char** argv) {
MyCap::instance = new MyCap();
MyCap::instance->capThreads.push_back(thread(MyCap::RunThreads, MyCap::instance));
// Outside loop.
while(MyCap::instance->keepRunning) {
}
for (int i = 0; i < MyCap::instance->capThreads.size(); i++) {
MyCap::instance->capThreads[i].join();
}
}
MyCap::MyCap() {
namedWindow("flow", 1);
cap.open(0);
}
void MyCap::RunThreads(MyCap * cap) {
cap->run();
}
void MyCap::run() {
// Inside loop.
while(keepRunning) {
cap >> frame;
imshow("flow", frame);
if (waitKey(30) >= 0) {
break;
}
}
}
With this code, I get a black screen. If I run cap.open(0) from within the run method, I don't even get that. I'm obviously doing something very wrong. But what really puzzles me is: why does it make a difference where that same code is called from? If I run what is now in run inside of main it will work. If I change the call of cap.open(0) from the constructor to run, that changes what the method does. Also the waitKey condition stops working from within the thread. What big thing am I missing?
Version 2
Based on the suggestions of #darien-pardibas, I made this second version:
Header:
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>
using namespace cv;
using namespace std;
class MyCap {
public:
MyCap();
void run();
bool keepRunning = true; // Will be changed by the external program.
static void RunThreads(MyCap * cap);
static vector<thread> capThreads;
static MyCap * getInstance();
private:
static MyCap * instance;
};
The main file:
#include "theprogram.h" // I'll admit that, even for a placeholder, it was a bad name.
MyCap * MyCap::instance = NULL;
vector<thread> MyCap::capThreads;
MyCap::MyCap() {
cout << "Instantiate" << endl;
}
MyCap * MyCap::getInstance() {
if (MyCap::instance == NULL) {
MyCap::instance = new MyCap;
}
return MyCap::instance;
}
void MyCap::RunThreads(MyCap * cap) {
cap->run();
}
void MyCap::run() {
cout << "Run" << endl;
namedWindow("flow", 1);
cout << "Window created." << endl;
VideoCapture cap(0); // HANGS HERE!
cout << "Camera open." << endl; // This never gets printed.
// Inside loop.
Mat frame;
while(keepRunning) {
cap >> frame;
imshow("flow", frame);
if (waitKey(30) >= 0) {
break;
}
}
}
int main(int argc, char** argv) {
MyCap::capThreads.push_back(thread(&MyCap::RunThreads, MyCap::getInstance()));
for (int i = 0; i < MyCap::capThreads.size(); i++) {
MyCap::capThreads[i].join();
}
}
This prints:
Instantiate
Run
Window created.
And hangs there.
But if I move the code from run to main and change keepRunning to true, then it works as expected. I think I'm missing something else, and I'm guessing it has something to do with how C++ works.
Okay, without looking at resolving all design patterns issues I can see in your code, I can confirm that the code below works. I think the main problem was that you needed to create the namedWindow in the same thread where you will be capturing the image and remove the while loop you had in your main method.
// "theheader.h"
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>
class MyCap {
public:
void run();
static void RunThreads(MyCap * cap);
bool keepRunning = true; // Will be changed by the external program.
std::vector<std::thread> capThreads;
private:
cv::Mat frame;
cv::VideoCapture cap;
MyCap() { }
static MyCap * s_instance;
public:
static MyCap *instance();
};
// "theheader.cpp"
#include "theheader.h"
#pragma comment(lib, "opencv_core248d")
#pragma comment(lib, "opencv_highgui248d")
using namespace std;
using namespace cv;
MyCap * MyCap::s_instance = NULL;
MyCap* MyCap::instance() {
if (s_instance == NULL)
s_instance = new MyCap();
return s_instance;
}
void MyCap::RunThreads(MyCap * cap) {
cap->run();
}
void MyCap::run() {
namedWindow("flow", 1);
cap.open(0);
// Inside loop.
while (keepRunning) {
cap >> frame;
imshow("flow", frame);
if (waitKey(30) >= 0) {
break;
}
}
}
int main(int argc, char** argv) {
MyCap::instance()->capThreads.push_back(thread(&MyCap::RunThreads, MyCap::instance()));
for (int i = 0; i < MyCap::instance()->capThreads.size(); i++) {
MyCap::instance()->capThreads[i].join();
}
}

OpenCV does not free camera

I'm trying to release the camera in OpenCV and reinitialize it another time, but the problem is, I cannot properly release the camera. Asking OpenCV, the cv::VideoCapture, whether the camer is open or not, it says closed, but the little LED is still glowing and I cannot create another VideoCapture object around the same camera.
This is the include I use:
#include <opencv2/opencv.hpp>
This is a little sample of code showing the problem:
cv::VideoCapture cap(0);
for(int i = 0; i < 20; i++) {
cv::Mat frame;
cap >> frame;
cv::imshow("Test", frame);
if (cv::waitKey(30) >= 0) { break; }
}
cap.release();
std::cout << "Camera is closed is : " << !cap.isOpened() << std::endl;
while(true) {
if (cv::waitKey(30) >= 0) { break; }
}
As already mentioned, the output says the camera is closed, but the LED is glowing. When I try to create a new VideoCapture around the camera it fails and says the camera is busy.
Any ideas?
This might be a bug of OpenCV 2.4.8 with some devices, you should check their bug tracker and post this issue there.
A fix for this problem might could be accomplished by putting the variable cap inside another scope:
{
cv::VideoCapture cap(0);
for(int i = 0; i < 20; i++) {
cv::Mat frame;
cap >> frame;
cv::imshow("Test", frame);
if (cv::waitKey(30) >= 0)
break;
}
}
/* At this point, cap was destroyed and your camera should be operational again */
{
cv::VideoCapture cap(0);
for(int i = 0; i < 20; i++) {
cv::Mat frame;
cap >> frame;
cv::imshow("AnotherTest", frame);
if (cv::waitKey(30) >= 0)
break;
}
}
The documentation says that the camera will be deinitialized automatically in VideoCapture destructor.
The code should be enough to fix the problem. But as you noticed, the problem remains, so could be 1 of 2 things:
This is really an issue inside OpenCV;
There's a bug in driver of the camera, so updating it might solve the problem.

Why does OpenCV emit "Bad flag in unknown function" for this code?

I have been trying to get a real basic video player working using the Open CV API. Everything seems to run smoothly until the end of a video clip, then i get this error:
OpenCV Error: Bad flag in unknown function, file ........\ocv\opencv\modules\core\src\array.cpp
This creates a break in the code at imshow("video", frame), i find this wierd as this is the part that effectively plays the video so why does it only kick up a fuss at the end of the clip?? I found that it seems to give me this error in the last 90% of every video i play so at the moment im working around it by telling it to stop once 90% of the clip has played but this isnt very good programming so can anyone send some suggestions/help?
I have looked at other peoples post on this matter and none of the solution suggested have worked for me as of yet.
Heres my code...its only an experimentation piece so im sorry if its a bit messy:
Thanks for any help in advance
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <direct.h>
#include <iostream>
using namespace cv;
void onTrackBarSlide(int);
double the_next_play_frame;
VideoCapture video("test.MOV"); // open the video file
int main(int, char**)
{
if(!video.isOpened()) // check if we succeeded
{
return -1;
}
int no_of_frames = video.get(CV_CAP_PROP_FRAME_COUNT); //total number of frames in video
std::cout << no_of_frames << std::endl;
std::cout << video.get(CV_CAP_PROP_FPS) << std::endl;
namedWindow("Video", 1);
cvCreateTrackbar("trackbar", "Video", 0, 40, onTrackBarSlide);
double stop_at = no_of_frames * 0.999;
for(;;){
if(the_next_play_frame >= double(stop_at))
{
break;
}
Mat frame;
video >> frame; // get a new frame from camera
imshow("Video", frame); // <---------- place where break/error occurs
if(waitKey(30) >= 0)
{
break;
}
}
return 0;
}
void onTrackBarSlide(int pos)
{
std::cout << getTrackbarPos("trackbar", "Video") << std::endl;
double frameratio = video.get(CV_CAP_PROP_FRAME_COUNT)/40; //10005 is the maximum value the slider can actual hold
double next_play_frame = frameratio * getTrackbarPos("trackbar", "Video");
video.set(CV_CAP_PROP_POS_FRAMES,next_play_frame);
the_next_play_frame = next_play_frame;
}
VideoCapture video("test.MOV"); // open the video file
int main(int, char**)
{
if(!video.isOpened()) // check if we succeeded
{
return -1;
}
}
Try put the VideoCapture instantiation inside main.
int main(){
VideoCapture video("test.MOV"); // open the video file
...
}