c++multithreadingopencvhighgui

cv::imshow does not display cv::mat color when on different thread


This is a class I use to spawn a HighGui window with some content on different thread.

class Capture {
private:
  bool running;
  std::thread thread;
  cv::Mat background;
  void loop() {
    while (running) {
      cv::imshow("sth",background);
      cv::waitKey(settings::capture_wait_time);
    }
  }
  public:
   Capture()  :
     running {false},
     thread {},
     background { 800, 800,  CV_8UC3, cv::Scalar{255,0,255}} {
       cv::namedWindow("sth");  
   }
   inline ~Capture() {
     if (running) stop(); // stop and join the thread
     cv::destroyWindow("sth");
   }
   void run() {
     if (!running) {
       running = true;
       thread = std::thread{[this]{loop();}};
     }
   }
   inline void join() { if (thread.joinable()) thread.join(); };
   inline void stop() {
     running = false;
     if (thread.joinable()) thread.join();
   }
};

// main
Capture cap;
cap.run();
// ... 

The problem is that the window will always end up being black (in this case it should be purple). I am obviously missing something here....


Solution

  • It seems that you cannot create a window in another thread. Also, the way you're calling the member function on the other thread seems wrong.

    Have a look at this code. It displays an image that change every second in a different thread, and returns after 5 seconds.

    #include <opencv2/opencv.hpp>
    #include <thread>
    
    using namespace std;
    using namespace cv;
    
    class Capture {
    private:
        bool running;
        std::thread thread;
        cv::Mat background;
        void loop() {
    
            while (running) {
                cv::imshow("sth", background);
                cv::waitKey(1000);
    
                Scalar color(rand()&255, rand()&255, rand()&255);
                background.setTo(color);
            }
        }
    public:
        Capture() :
            running{ false },
            thread{},
            background{ 800, 800, CV_8UC3, cv::Scalar{ 255, 0, 255 } } {
        }
        inline ~Capture() {
            if (running) stop(); // stop and join the thread
        }
        void run() {
            if (!running) {
                running = true;
                thread = std::thread{ &Capture::loop, this };
            }
        }
        inline void join() { if (thread.joinable()) thread.join(); };
        inline void stop() {
            running = false;
            if (thread.joinable()) {
                thread.join();
            }
        }
    };
    
    int main()
    {
        Capture cap;
        cap.run();
    
        std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    
        cap.stop();
    
        return 0;
    }