在多线程应用程序

时间:2016-09-26 10:50:15

标签: c++ multithreading qt opencv openni

我有一个使用Qt5.7和OpenNI用C ++编写的多线程应用程序。它有一个主线程,启动第二个线程,从.oni记录文件(asus xtion pro live)捕获帧进行一些处理,并通过Qt信号槽机制将帧传递给主线程,使用{{ 1}}。 我想要做的是实现一个暂停键,所以按下例如'p'处理暂停。我在考虑这样的事情:

imshow()

通过这种方式它不起作用,我认为那是因为框架是由另一个线程(主要的)显示的,void Camera::run(){ while(!cameraStop && this->device.isValid()) { try { if (!buttonPause) { getFrame(); process(); emit sigFrameImageReady(frame); if (cv::waitKey(1)==112){ setButtonPause(!(getButtonPause())); } } } catch(std::exception &ex) { std::cerr << "getFrame()" << ex.what() << std::endl; } } } 这里只是阻止整个过程,但是如果我把它放在主要的线程,就在这个waitKey()之后:

imshow()

waitkey似乎被忽略了(图像显示工作正常)..任何想法?

修改的 GUI部分仅用于调试目的。

2 个答案:

答案 0 :(得分:1)

您应该在显示线程中实现线程安全FIFO缓冲区或循环缓冲区。来自相机线程的信号将图像推送到此缓冲区,显示线程将把它们取出并在单独的循环中显示它们。只有这样才能将相机事件循环与显示线程分开。

答案 1 :(得分:0)

也许这是一个较晚的答案,但是我想介绍一下我在最近的项目中所做的事情,以防万一有人处于类似情况并希望使用相同的技术。

我的项目基本上是一个命令行程序,并使用QT进行多线程和线程间通信,同时我们还需要一些最小的UI来显示图像并处理用户按键事件,因此OpenCV Highgui模块对我来说已经足够了。这是我为使它们一起工作所做的:

  1. (重要),您必须构建具有QT支持的OpenCV才能使 以下代码可以正常工作,即在构建OpenCV时在cmake-gui中选中WITH_QT选项。或者,如果您使用vcpkg,请使用:

    vcpkg install opencv[qt]:x64-windows
    
  2. 首先为工作线程创建类,我使用该线程不断从相机缓冲区中检索图像帧,并在每次图像准备就绪时通知主线程:

    class MyThread : public QThread
    {
        Q_OBJECT
    
    public:
        MyThread() : mAbort(false)
        {
        }
    
        ~MyThread()
        {
            mMutex.lock();
            mAbort = true; // make the thread out of the loop
            mMutex.unlock();
    
            wait(); // wait until the run() function returns
            cout << "thread terminated" << endl;
        }
    
    protected:
        void run() override 
        {
            while(!mAbort) {
                cv::Mat frame;
                if (myCamera->grab(frame) && !frame.empty()) {
                    emit imageReady(frame);
                }
                else {
                    cout << "failed to grab" << endl;
                }
            }
        }
    
    signals:
        void imageReady(const cv::Mat& frame);
    
    private:
        bool mAbort;
        QMutex mMutex;
    };
    
  3. 然后创建控制器类来处理来自工作线程的信号,我用它来显示框架。注意它正在主线程中运行:

    class MyController : public QObject
    {
        Q_OBJECT
    
    public:
        MyController(MyThread* pThread) : mThread(pThread)
        {
            // this is needed as the argument is passed through queued connection
            qRegisterMetaType<cv::Mat>("CvMat");
            connect(mThread, &MyThread::imageReady, this, &MyController::onImageReady);
            connect(mThread, &MyThread::finished, mThread, &QObject::deleteLater);
    
            mThread->start();
        }
    
    public slots:
        void onImageReady(const cv::Mat& frame) {
            cv::imshow("camera live view", frame);
        }
    
    private:
        MyThread* mThread;
    };
    
  4. 现在在main函数中,我们可以启动线程并使用cv::waitKey()处理键事件,请注意,它还将在内部启动QT main事件循环。

    Q_DECLARE_METATYPE(cv::Mat)
    ...
    int main(int argc, char* argv[])
    {
        ...
        // start working thread
        MyThread thread = new MyThread();
        MyController controller(thread);
    
        // handle key events
        while (true) {
            int key = cv::waitKey();
            cout << "key = " << key << endl;
    
            // press "ESC" to exit
            if (key == 27) {
                break;
            }
        }
    
        // clean up
        ...
        delete thread;
        delete myCamera;
        return 0;
    }