Cannot write to OpenCV frame captured by a different thread - c++

By using the following code, I can successfully capture and return frame from a separate thread to main(). But now I cannot write to the frame/cv::Mat (contour in main()) for display. If I do not use separate thread to for webcam then I can successfully write to the frame/cv::Mat. What am I doing wrong?
VideoCapture cap(0);
class Camera
{
public:
Camera(void);
~Camera(void);
Mat captureVideo(void);
private:
Mat frame;
double dWidth;
double dHeight;
double fps;
};
Camera::Camera(void) {
int isrunning = 0;
usleep(10);
if (!cap.open(2))
{
cout << "Cannot open the video camera.\n" << endl;
}
else
{
isrunning = 1;
}
if(isrunning == 0) {
this->~Camera();
}
cap >> frame;
}
Camera::~Camera(void) {
cap.release();
}
Mat Camera::captureVideo(void) {
cap >> frame;
return frame;
}
Camera cam1;
const int frameBuffer = 20; // Frame buffer
std::vector<cv::Mat> frameStack;
int stopSig = 0; // Global stop signal...
void grabFrame(void) {
Mat frame;
::frameStack.clear();
while(!::stopSig) {
frame = ::cam1.captureVideo();
// 1. Remove one frame from the back, if the stack has more then 2 frames...
if(::frameStack.size() > 2) { //If the framestack has more then 2 frames...
::frameStack.pop_back();
}
// 2. Add a frame at the front of the stack if the stack is not full...
if (::frameStack.size() < ::frameBuffer) {
::frameStack.push_back(frame); // Put new frame on stack on the computer's RAM...
} else {
::frameStack.clear();
}
}
return;
}
int main(int argc, char* argv[])
{
Mat fr, outImg; // Captured single frames...
Mat contour; // Video stream showin countours of objects...
::frameStack.clear();
thread t1(grabFrame);
cv::namedWindow("MyWindow", 1);
while(1) {
if(::frameStack.size() >= 2) {
contour = ::frameStack.back();
circle(contour, Point(150,150),50, Scalar(0,255,255),cv::FILLED, 8);// PROBLEM -> NO EFFECT
putText(contour, "some text", Point(100,100), FONT_HERSHEY_DUPLEX, 1, Scalar(0,143,143), 2);// PROBLEM -> NO EFFECT
imshow("Contour Video", contour);
}
if (waitKey(1) == 27)
{
::stopSig = 1; // Signal to threads to end their run...
frameStack.clear();
break;
}
}
t1.join();
return 0;
}

In your example, you are sharing stopSig, frameStack and frameBuffer variables between the two threads.
When you are using shared memory between threads, you have to properly synchronize the access to that shared memory. One of the reasons why synchronization is needed is to prevent two threads modifying a variable at the same time. Another reason is visibility - a write to a variable by one thread can be cached inside the CPU cache, and a thread running on another CPU core won't see the variable change.
Perhaps the easiest way to synchronize memory access between threads is by using std::mutex. I'm not gonna post the whole code here, but you can read about mutexes in C++ online, for example, here.

Related

How to efficiently seek in video files with OpenCV?

I want to make a common video player with opencv C++. Users should be able to freely move between frames of the video(commonly slider). So I simply wrote and tested two methods and both has problems.
My first solution:
int frameIdx = 0; /// This is accessed by other thread
cv::VideoCapture cap("video.mp4");
while (true) {
cv::Mat frame;
cap.set(cv::CAP_PROP_POS_FRAMES, frameIdx);
cap.read(frame);
showFrameToWindow(frame);
frameIdx++;
}
My second solution:
int frameIdx = 0; /// This is accessed by other thread
std::vector<cv::Mat> buffer;
cv::VideoCapture cap("video.mp4")
while (true) {
cv::Mat frame;
cap >> frame;
if (frame.empty()) break;
buffer.push_back(frame);
}
while (true) {
cv::Mat frame = buffer[frameIdx].clone();
showFrameToWindow(frame);
frameIdx++;
}
The first solution is too slow. I think there is an overhead cap.read(cv::Mat). It's not possible to play video more than 20~30fps in my computer.
The second solution, it is satisfactory for speed, but it requires a lot of memory space.
So I imagine what if I change std::vector to std::queue of the buffer, limit its size, and update it in other thread while playing the video.
I'm not sure it's gonna work, and I wonder there's an common algorithm to seek in large video file. Any comments will save me. Thanks.
I developed my second solution to limitied size of frame buffer and handling it with other thread.
/// These variables are accessed from all threads.
#define BUF_MAX 64
bool s_videoFileLoaded;
double s_videoFileHz; // milliseconds per frame
int s_videoFileFrameCount;
bool s_seek;
int s_seekingIndex; // indexing video file, binded with UI
std::queue<cv::Mat> s_frameBuffer;
std::queue<int> s_indexBuffer;
std::mutex s_mtx;
///
/// !!! When seeking slider in UI moved manually,
/// !!! #s_seek turns to True.
///
/// Start of main thread.
cv::VideoCapture cap("video.mp4");
s_videoFileLoaded = true;
s_videoFileFrameCount = cap.get(cv::CAP_PROP_FRAME_COUNT);
s_videoFileHz = 1000.0 / cap.get(cv::CAP_PROP_FPS);
s_seekingIndex = 0;
runThread( std::bind(&_videoHandlingThreadLoop, cap) );
Timer timer;
while (s_videoFileLoaded) {
timer.restart();
{
std::lock_guard<std::mutex> lock(s_mtx);
if (s_frameBuffer.empty())
continue;
cv::Mat frame = s_frameBuffer.front();
s_seekingIndex = s_indexBuffer.front();
s_frameBuffer.pop();
s_indexBuffer.pop();
showFrameToWindow(frame);
}
int remain = s_videoFileHz - timer.elapsed();
if (remain > 0) Sleep(remain);
}
void _videoHandlingThreadLoop(cv::VideoCapture& cap) {
s_seek = true;
int frameIndex = -1;
while (s_videoFileLoaded) {
if (s_frameBuffer.size() > BUF_MAX) {
Sleep(s_videoFileHz * BUF_MAX);
continue;
}
// Check whether it's time to 'seeking'.
if (s_seek) {
std::lock_guard<std::mutex> lock(s_mtx);
// Clear buffer
s_frameBuffer = std::queue<cv::Mat>();
s_indexBuffer = std::queue<int>();
frameIndex = s_seekingIndex;
cap.set(cv::CAP_PROP_POS_FRAMES, frameIndex);
s_seek = false;
}
// Read frame from the file and push to buffer.
cv::Mat frame;
if (cap.read(frame)) {
std::lock_guard<std::mutex> lock(s_mtx);
s_frameBuffer.push(frame);
s_indexBuffer.push(frameIndex);
frameIndex++;
}
// Check whether the frame is end of the file.
if (frameIndex >= s_videoFileFrameCount) {
s_seekingIndex = 0;
s_seek = true;
}
}
}
and this worked. I could play the video file with stable playback speed. But still had some lag when seeking manually.

Unknown pooling method when testing caffe with cuda but not cudnn

I built the caffe deep learning library in windows as shown in this link:
https://initialneil.wordpress.com/2015/07/15/caffe-vs2013-opencv-in-windows-tutorial-i/
I deactivated the cuDNN because my nvidia card didnot support this and changed the targert architecture to fermi architecture.
I built caffe as static library to use it in the test project shown below:
int main(int argc, char** argv)
{
// get a testing image and display
Mat img = imread(CAFFE_ROOT + "/examples/images/mnist_5.png");
cvtColor(img, img, CV_BGR2GRAY);
imshow("img", img);
waitKey(1);
// Set up Caffe
Caffe::set_mode(Caffe::GPU);
int device_id = 0;
Caffe::SetDevice(device_id);
LOG(INFO) << "Using GPU";
// Load net
Net<float> net(CAFFE_ROOT + "/examples/mnist/lenet_test-memory-1.prototxt");
string model_file = CAFFE_ROOT + "/examples/mnist/lenet_iter_10000.caffemodel";
net.CopyTrainedLayersFrom(model_file);
// set the patch for testing
vector<Mat> patches;
patches.push_back(img);
// push vector<Mat> to data layer
float loss = 0.0;
boost::shared_ptr<MemoryDataLayer<float> > memory_data_layer;
memory_data_layer = boost::static_pointer_cast<MemoryDataLayer<float>>(net.layer_by_name("data"));
vector<int> labels(patches.size());
memory_data_layer->AddMatVector(patches, labels);
// Net forward
//ERROR IN THE LINE BELOW
const vector<Blob<float>*> & results = net.ForwardPrefilled(&loss);// HERE THE ERROR
float *output = results[1]->mutable_cpu_data();
// Display the output
for (int i = 0; i < 10; i++) {
printf("Probability to be Number %d is %.3f\n", i, output[i]);
}
waitKey(0);
}
But I get an error when accessing the file: pooling_layer.cu in the function described below:
template <typename Dtype>
void PoolingLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top) {
const Dtype* bottom_data = bottom[0]->gpu_data();
Dtype* top_data = (*top)[0]->mutable_gpu_data();
int count = (*top)[0]->count();
// We'll output the mask to top[1] if it's of size >1.
const bool use_top_mask = top->size() > 1;
int* mask = NULL;
Dtype* top_mask = NULL;
switch (this->layer_param_.pooling_param().pool()) {
case PoolingParameter_PoolMethod_MAX:
if (use_top_mask) {
top_mask = (*top)[1]->mutable_gpu_data();
} else {
mask = max_idx_.mutable_gpu_data();
}
// NOLINT_NEXT_LINE(whitespace/operators)
MaxPoolForward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>> (
count, bottom_data, bottom[0]->num(), channels_,
height_, width_, pooled_height_, pooled_width_, kernel_h_,
kernel_w_, stride_h_, stride_w_, pad_h_, pad_w_, top_data,
mask, top_mask);
break;
case PoolingParameter_PoolMethod_AVE:
// NOLINT_NEXT_LINE(whitespace/operators)
AvePoolForward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
count, bottom_data, bottom[0]->num(), channels_,
height_, width_, pooled_height_, pooled_width_, kernel_h_,
kernel_w_, stride_h_, stride_w_, pad_h_, pad_w_, top_data);
break;
case PoolingParameter_PoolMethod_STOCHASTIC:
if (Caffe::phase() == Caffe::TRAIN) {
// We need to create the random index as well.
caffe_gpu_rng_uniform(count, Dtype(0), Dtype(1),
rand_idx_.mutable_gpu_data());
// NOLINT_NEXT_LINE(whitespace/operators)
StoPoolForwardTrain<Dtype><<<CAFFE_GET_BLOCKS(count),
CAFFE_CUDA_NUM_THREADS>>>(
count, bottom_data, bottom[0]->num(), channels_,
height_, width_, pooled_height_, pooled_width_, kernel_h_,
kernel_w_, stride_h_, stride_w_,
rand_idx_.mutable_gpu_data(), top_data);
} else {
// NOLINT_NEXT_LINE(whitespace/operators)
StoPoolForwardTest<Dtype><<<CAFFE_GET_BLOCKS(count),
CAFFE_CUDA_NUM_THREADS>>>(
count, bottom_data, bottom[0]->num(), channels_,
height_, width_, pooled_height_, pooled_width_, kernel_h_,
kernel_w_, stride_h_, stride_w_, top_data);
}
break;
default:
LOG(FATAL) << "Unknown pooling method.";
}
CUDA_POST_KERNEL_CHECK;
}
And get the message "Unknown pooling method." as shown in the window below:
The normal execution of my project is described in the image below:
Could someone give me an idea about the possible solution?
The pooling layer which by default should be max pooling was translated into some other layers. You might add a breakpoint at pooling_layer.cu (line 163) or add cout << this->layer_param_.pooling_param().pool() << endl; before that line to see what pooling layer it was using. I guess it doesn't equal to PoolingParameter_PoolMethod_MAX here.
I'm not sure why it happened, maybe there some error in the prototxt file or the protobuf. A brutal trick would be overlapping line 206 with line 165-176 in order to force using max pooling.

Error when trying to access a pixel value in OpenCV

I just started learning c++ and OpenCV. I've tried to write a program that allows me to click on a pixel and print out the pixel value in BGR. So far, when I click, my program returns the coordinate of the clicked pixel but when I want to access the BGR-value my program crashes. I'm streaming video from my webcam as input and I don't get any errors when i compile.
I've been reading a lot of similar questions in here but I haven't found any solution.
My program crashes when I include the line:
"cv::Vec3f BGR = matOriginal.at(click[0][0],click[0][1]);"
The rest of my program is as follows:
int click[1][2];
void mouseEvent(int Event, int x, int y, int flags, void* param)
{
if(Event == CV_EVENT_LBUTTONDOWN)
{
printf("Valgt pixel: [%d,%d] \n ",x,y);
click[0][0] = x;
click[0][1] = y;
}
}
int main()
{
VideoCapture capWebcam(0);
if(capWebcam.isOpened() == false)
{
printf("Error: capWebcam not accessed successfully \n");
return(1);
}
Mat matOriginal;
char charCheckForEscKey=0;
while(charCheckForEscKey !=27)
{
if(capWebcam.read(matOriginal) == NULL)
{
printf("Error: Frame not read\n \n");
break;
}
cv::imshow("Original",matOriginal);
setMouseCallback("Original",mouseEvent,0);
// Print BGR-value when mouseclick
if(click[0][0]>0 && click[0][1]>0)
{
cv::Vec3f BGR = matOriginal.at<cv::Vec3f>(click[0][0],click[0][1]); // When this statement is included, I get an ERROR
double B=BGR[0]; // B
double G=BGR[1]; // G
double R=BGR[2]; // R
click[0][0] = 0; // Resetting the click-matrix
click[0][1] = 0;
}
charCheckForEscKey = cv::waitKey(10);
}
return(0);
}
I'm using Microsoft Visual C++ 2013 Express
Thanks in advance for your help
Ok, two different things could be going on here:
The crash happens because click is not initialized:
When the execution of your program reaches the while loop, and the user has not pressed the mouse button, there's no way to tell what values are stored by click since they are most probably garbage from the memory.
This means that when you program first runs, click could be storing values larger than the dimensions of matOriginal, thus rendering you a crash for trying to access indexes that doesn't exist.
Always initialize your variables!
int main()
{
click[0][0] = -1;
click[0][1] = -1;
// ...
}
What I just said could be tested by printing these values to the screen before accessing them:
std::cout << "X: " << click[0][0] << " Y: " << click[0][1] << std::endl;
cv::Vec3f BGR = matOriginal.at<cv::Vec3f>(click[0][0], click[0][1]);
The crash happens because cv::Mat is expecting the X,Y (row,column) values in the reverse order (column,row):
I think that's most likely the cause of the problem. Try:
cv::Vec3f BGR = matOriginal.at<cv::Vec3f>(click[0][1], click[0][0]);

OpenCV Error: insufficient memory, in function call

I have a function looks like this:
void foo(){
Mat mat(50000, 200, CV_32FC1);
/* some manipulation using mat */
}
Then after several loops (in each loop, I call foo() once), it gives an error:
OpenCV Error: insufficient memory when allocating (about 1G) memory.
In my understanding, the Mat is local and once foo() returns, it is automatically de-allocated, so I am wondering why it leaks.
And it leaks on some data, but not all of them.
Here is my actual code:
bool VidBOW::readFeatPoints(int sidx, int eidx, cv::Mat &keys, cv::Mat &descs, cv::Mat &codes, int &barrier) {
// initialize buffers for keys and descriptors
int num = 50000; /// a large number
int nDims = 0; /// feature dimensions
if (featName == "STIP")
nDims = 162;
Mat descsBuff(num, nDims, CV_32FC1);
Mat keysBuff(num, 3, CV_32FC1);
Mat codesBuff(num, 3000, CV_64FC1);
// move overlapping codes from a previous window to buffer
int idxPre = -1;
int numPre = keys.rows;
int numMov = 0; /// number of overlapping points to move
for (int i = 0; i < numPre; ++i) {
if (keys.at<float>(i, 0) >= sidx) {
idxPre = i;
break;
}
}
if (idxPre > 0) {
numMov = numPre - idxPre;
keys.rowRange(idxPre, numPre).copyTo(keysBuff.rowRange(0, numMov));
codes.rowRange(idxPre, numPre).copyTo(codesBuff.rowRange(0, numMov));
}
// the starting row in code matrix where new codes from the updated features to add in
barrier = numMov;
// read keys and descriptors from feature file
int count = 0; /// number of new points that are read in buffers
if (featName == "STIP")
count = readSTIPFeatPoints(numMov, eidx, keysBuff, descsBuff);
// update keys, descriptors and codes matrix
descsBuff.rowRange(0, count).copyTo(descs);
keysBuff.rowRange(0, numMov+count).copyTo(keys);
codesBuff.rowRange(0, numMov+count).copyTo(codes);
// see if reaching the end of a feature file
bool flag = false;
if (feof(fpfeat))
flag = true;
return flag;
}
You don't post the code that calls your function, so I can't tell whether this is a true memory leak. The Mat objects that you allocate inside readFeatPoints() will be deallocated correctly, so there are no memory leaks that I can see.
You declare Mat codesBuff(num, 3000, CV_64FC1);. With num = 5000, this means you're trying to allocate 1.2 gigabytes of memory in one big block. You also copy some of this data to codes with the line:
codesBuff.rowRange(0, numMov+count).copyTo(codes);
If the value of numMove + count changes between iterations, this will cause reallocation of the data buffer in codes. If the value is large enough, you may also be eating up a significant amount of memory that persists across iterations of your loop. Both of these things may be leading to heap fragmentation. If at any point there doesn't exist a 1.2 GB chunk of memory waiting around, an insufficient memory error occurs, which is what you have experienced.

Error in using cvVideoWriter in opencv?

This code snippet is supposed to save part of a video whose range is defined by start and end. There is an array of structures (data[i]) that holds the starting and end frame of a video shot in the original video. There are total of 8 shots.
for (int i = 0; i < finalCount-1; ++i) {
capture = cvCaptureFromAVI("Stats\\Shots\\Cricketc1.avi");
assert(capture);
int frame_number = 0;
int start = data[i].start_frame;
int end = data[i].end_frame;
char shotname[100];
strcpy_s(shotname, "shot_");
char shot_id[30];
_itoa_s(data[i].shot_no, shot_id, 10);
strcat_s(shotname, shot_id);
strcat_s(shotname, ".avi");
IplImage* image = NULL;
CvVideoWriter* writer = NULL;
writer = cvCreateVideoWriter (shotname, CV_FOURCC('i','Y','U','V'), fps, cvSize(width, height), 1);
assert(writer);
while (frame_number >= start && frame_number < end) {
image = cvQueryFrame(capture);
assert(image);
cvWriteFrame(writer, image);
}
cvReleaseImage(&image);
cvReleaseVideoWriter(&writer);
cvReleaseCapture(&capture);
cout << shotname << " saved ..." << endl;
}
After running the program 8 video files are created that have a size of 6kb and do not run. I have tried various codecs like divx, mjpg, mpg2, iyuv etc but all give the same result.
In your while loop, frame_number is never incremented. Since you say the program actually executes and creates the files this means nothing in your while loop ever runs... otherwise you'd get stuck in an infinite loop because frame_number will always be 0.
I would advise you initialize frame_number to start instead of 0 and there's no reason for it to exist outside of the scope of the loop so a for seems more appropriate:
int start = data[i].start_frame;
int end = data[i].end_frame;
...
for (int frame_number = start; frame_number < end; frame_number++) {
image = cvQueryFrame(capture);
assert(image);
cvWriteFrame(writer, image);
}
If Gunther Fox answer won't help try to use different codec - it's very strange, but in my situation iyuv is not working at all and some other codecs works ok, but i can't read them while debugging... For me - ms video and radius cinepak always works fine(writing and reading), iyuv is not working at all, other codes - writing and reading, but not while debugging.