3

I have two tasks (Threads) each task runs on a different processor(core), the first capture an image repeatedly using OpenCV videocapture().

I only used these two lines for the capture :

cv::Mat frame;
capture.read(frame);

Now I want to display the captured image using the second task. After executing the imshow function within the second task's code :

cv::imshow("Display window", frame);

I got the following output error :

OpenCV Error: Assertion failed (size.width>0 && size.height>0) in imshow, file /build/opencv-L2vuMj/opencv-3.2.0+dfsg/modules/highgui/src/window.cpp, line 304
terminate called after throwing an instance of 'cv::Exception'
what():  /build/opencv-L2vuMj/opencv-3.2.0+dfsg/modules/highgui/src/window.cpp:304: error: (-215) size.width>0 && size.height>0 in function imshow

So, how can I avoid this error?

The complete code is hosted at Github

asendjasni
  • 963
  • 1
  • 16
  • 36
  • 1
    make the access to "frame" mutual exclusive, so that a thread can't modify and image, while the other one displays it. – Micka Feb 06 '19 at 08:29
  • 1
    Whilst a mutex will work, another option is to have a vector of 2 to 8 Mats and read the images into the Mats in a round-robin fashion... first image goes in Mat 0, second image into Mat 1, third image onto Mat 2 and use condwait() in the display task to wait for each Mat. That allows both the capture and the display task to make progress at the same time. – Mark Setchell Feb 06 '19 at 09:49
  • @MarkSetchell And wouldn't that be a little bit greedy. – asendjasni Feb 06 '19 at 09:52
  • Greedy? In what sense? A full-colour 1080p image is only 6MB. Many people have 8GB of RAM on their laptops just to run a word processor so that is less than 0.1% of the RAM and image processing is far more deserving of RAM than Microsoft Word! Even a Raspberry Pi has 1GB of RAM, so what is 6MB? – Mark Setchell Feb 06 '19 at 09:56
  • @MarkSetchell Sorry, I mean in term of time execution. Cuz this is a part of a real-time project and I'm trying to minimize the capturing and displaying time under 20 MILLS. – asendjasni Feb 06 '19 at 09:57
  • 1
    If that's the case, use of double-buffering is **absolutely ideal**. If your camera can take 30fps, that means it will take 33ms to acquire a frame. If you use a mutex, that means those 33ms are completely lost to the display task! If you have 2 buffers, your camera can be acquiring the next frame whilst you display the current one. – Mark Setchell Feb 06 '19 at 10:01
  • @MarkSetchell That what I was looking for. I did some digging regarding the double-buffering technique but I had some difficulties implementing it. – asendjasni Feb 06 '19 at 10:03
  • The answer from @Spinkoo should be a great start into how to do it properly as it shows you the `pthread_cond_wait()` stuff, so, personally, I would take that and adapt it to using 2, or maybe more buffers. Definitely deserves some upvotes... – Mark Setchell Feb 06 '19 at 10:08
  • Yes, I already updated my code using @Spinkoo idea. By two, you mean two `cv::Mat frame_1, frame_2` or do you mean something else. – asendjasni Feb 06 '19 at 10:12
  • I actually meant a vector of Mats, `frame[0]`, `frame[1]`... `frame[N-1]` but your two will also work. – Mark Setchell Feb 06 '19 at 10:30
  • @MarkSetchell I will do my best so that I can implement the double-buffering technique. Can I refer to you if I'll encounter some trouble? – asendjasni Feb 06 '19 at 10:32
  • Questions, and answers, are free on StackOverflow. There are plenty of very talented, knowledgeable people willing to help anyone who tries to help themselves. So, just ask a new, sensible question, which you have already researched and done your best to tackle and add **Minimum Complete and Verifiable Code** and it is pretty likely someone will help you out. Good luck with your project! – Mark Setchell Feb 06 '19 at 10:35
  • @MarkSetchell Thank you. – asendjasni Feb 06 '19 at 10:48

4 Answers4

3

cv::VideoCapture::read() returns bool indicating if the read was successful or not.

You are passing an empty frame to cv::imshow(). Try checking if the read was successful before trying to show it.

cv::Mat frame;
if(capture.read(frame))
{
    cv::imshow(frame);
}

EDIT

OP posted a link to the code. In his program frame is declared as a global variable. In line 120 capture.read(frame) writing into the frame and in line 140 imshow(frame) reads from the frame without using a mutex - that's a data race. Correct code should be along the lines of:

#include <mutex>
#include <opencv2/opencv.hpp>

std::mutex mutex;
cv::Mat frame;

{
    mutex.lock();
    capture.read(frame);
    mutex.unlock();
}
{
    mutex.lock();
    cv::imshow(frame);
    mutex.unlock();
}
serkan.tuerker
  • 1,681
  • 10
  • 20
  • I did check that, using only a single task that performs both capturing and displaying was a success. – asendjasni Feb 06 '19 at 08:04
  • 1
    But with two "tasks" you are getting this error? If your "tasks" are two parallel threads, then you are probably having a data race. – serkan.tuerker Feb 06 '19 at 08:07
  • actually the tasks are launched simultaneously with a different offset. – asendjasni Feb 06 '19 at 08:09
  • 1
    In your program `frame` is declared as a global variable. In line `120` you are writing into the `frame` and in line `140` you are basicallt reading from it (`imshow`) without using a [mutex](https://stackoverflow.com/questions/34524/what-is-a-mutex) - that's a data race. – serkan.tuerker Feb 06 '19 at 08:12
  • I just tried that and it's worked, thanks. Could you take a look at the code if I used it properly? – asendjasni Feb 06 '19 at 08:22
  • 1
    See my edited post. I am not that familiar with `pthread`, so I can't tell you much. But I am glad that it works now. – serkan.tuerker Feb 06 '19 at 08:29
2

The problem with your code is that there is a data race.. Imagine the display thread goes first lock the frame & try to display it before it is read so as you can see the problem If you want a synchronized solution you can use the pthread condition & wait till an image is read to signal your display function otherwise you are gonna have an active wait!!

// in the declaration part 
// Declaration of thread condition variable 
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; 

//in the display function 

ptask DisplyingImageTask()
{

    int task_job = 0;

    while (1)
    {
        std::cout << "The job " << task_job << " of Task T" << ptask_get_index()
                  << " is running on core " << sched_getcpu() << " at time : "
                  << ptask_gettime(MILLI) << std::endl;

        cv::namedWindow("Display window", cv::WINDOW_AUTOSIZE);

        pthread_mutex_lock(&frame_rw);
        //wait for the read function to send a signal
        pthread_cond_wait(&cond1, &frame_rw);

        cv::imshow("Display window", frame);        
        cv::waitKey(1);

        pthread_mutex_unlock(&frame_rw);

        ptask_wait_for_period();
        task_job++;
    }

}

// in the Read function

ptask CapturingImageTask()
{

    int task_job = 0;

    while (1)
    {
        std::cout << "The job " << task_job << " of Task T" << ptask_get_index()
                  << " is running on core " << sched_getcpu() << " at time : "
                  << ptask_gettime(MILLI) << std::endl;

        pthread_mutex_lock(&frame_rw);
        capture.read(frame);
        //after capturing the frame signal the display function & everything should be synchronize as you want 
        pthread_cond_signal(&cond1);  

        pthread_mutex_unlock(&frame_rw);

        ptask_wait_for_period();
        task_job++;
    }

}
Spinkoo
  • 2,080
  • 1
  • 7
  • 23
  • I just used your solution and it worked perfectly fine. Thanks. – asendjasni Feb 06 '19 at 10:07
  • Do you have any suggestion regarding the double buffering technique that @MarkSetchell mentioned in the comments above? – asendjasni Feb 06 '19 at 10:20
  • 1
    I totally agree with his suggestion.. this would make the work more synchronized – Spinkoo Feb 06 '19 at 10:47
  • I don't know anything about OpenCV, but the docs say that `capture.read` returns a `bool` that indicates either success or failure, so… wouldn't it be a great idea to check that value? – Arne Vogel Feb 06 '19 at 12:26
1
int main()
{
VideoCapture cap;
while(1){
  Mat frame;
  cap >> frame;
  imshow("frame",frame);
  waitKey();}
}

You can try this. İf you write waitKey(); Code want to press any key for get frame and show frame.

asendjasni
  • 963
  • 1
  • 16
  • 36
Cevdet
  • 69
  • 9
  • Thanks for your response. It's not exactly what I need. The capturing and the displaying are considered as a distinct process. – asendjasni Feb 06 '19 at 07:58
  • 1
    So describe public Mat value and assign frame to Mat value. Then You can read Mat value another process. – Cevdet Feb 06 '19 at 08:05
1

As others have mentioned try using a mutex.

You can also have a condition on the cv::Mat before trying to display it:

if (frame.data()) 
    imshow("window", frame);

This will check that the frame to be displayed has data and thus avoiding the error.

Again this condition is only to avoid the imshow error and not to solve the original problem which, as mentioned in other answers, is a data race between the 2 threads.

EYakoumi
  • 407
  • 6
  • 17