与另一个线程中的循环进行通信

时间:2015-05-11 06:17:29

标签: c++ multithreading qt opencv

所以我的任务就是这个 - 我有一个带有HSV值滑块的GUI线程(以及其他东西),以及一个工作线程,它完成所有OpenCV工作并将处理过的视频图像发送回GUI线程。

通常情况下,OpenCV工作在无限循环中。问题是,一半的工作是根据GUI滑块发送的HSV值转换当前视频帧。如果在循环开始之前发送,它就可以工作。但不是在它发生的时候,我需要它在飞行中工作。

有没有好的方法与该线程通信并更改OpenCV循环使用的HSV值,还是傻瓜的差事?我可以想到两个解决方案,其中一个可能非常低效(涉及将值保存到文件中)。我是Qt的新手,我可能很容易错过文档和教程中的内容。

编辑: 这是我的应用程序的工作方式 - 在GUI线程中,用户选择一个文件。带有url的信号被发送到工作线程,该线程开始工作。当用户更改HSV值时,将发送信号以更改另一个线程的值。如果循环尚未启动,则会收到它们,QDebug会向我显示。

EDIT2: 我可能一直在想这一切都错了。有没有办法让线程从另一个中提取值?而不是等待他们被发送?

EDIT3: kalibracja.cpp,适用于Micka。

int hueMin=0;
int hueMax=180;
int satMin=0;
int satMax=255;
int valMin=15;
int valMax=255;
int satMinDua=133; //tests 

HSV::HSV(QObject * parent) : QObject(parent)
{
    hsvThread  = new QThread;
    hsvThread ->start();
    moveToThread( hsvThread  );
}

HSV::~HSV() //destruktor
{
    hsvThread ->exit(0);
    hsvThread ->wait();
    delete hsvThread ;
}

void HSV::processFrames(QString kalibracja) {

    while(1) {
        cv::VideoCapture kalibrowanyPlik;
        kalibrowanyPlik.open(kalibracja.toStdString());
        int maxFrames = kalibrowanyPlik.get(CV_CAP_PROP_FRAME_COUNT);
        for(int i=0; i<maxFrames; i++)
        {
            cv::Mat frame;
            cv::Mat gray;

            //satMin=kontenerHsv->satMin;
            qDebug() << "kalibracja satMin - " << satMin;
            qDebug() << "fdfdf - " << satMinDua;

            kalibrowanyPlik.read(frame);
            cv::cvtColor(frame, gray, CV_BGR2GRAY);

            QImage image(cvMatToQImage(frame));
            QImage processedImage(cvMatToQImage(gray));

            emit progressChanged(image, processedImage);
            QThread::msleep(750); //so I can read qDebug() messages easly
        }
    }
}

void MainWindow::onProgressChagned(QImage image, QImage processedImage) {
    QPixmap processed = QPixmap::fromImage(processedImage);
    processed = processed.scaledToHeight(379);
    ui->labelHsv->clear();
    ui->labelHsv->setPixmap(processed);

    QPixmap original = QPixmap::fromImage(image);
    original = original.scaledToHeight(379);
    ui->labelKalibracja->clear();
    ui->labelKalibracja->setPixmap(original);
}

void HSV::updateHsv(QString hmin, QString hmax, QString smin, QString smax, QString vmin, QString vmax){

    satMinDua=smin.toInt();

}

mainwindow.cpp连接

HSV *hsv = new HSV;
(.... all kinds of things ....)
void MainWindow::updateHsvValues() {
QMetaObject::invokeMethod(hsv, "updateHsv", Qt::QueuedConnection,
                              Q_ARG(QString, hmin),
                              Q_ARG(QString, hmax),
                              Q_ARG(QString, smin),
                              Q_ARG(QString, smax),
                              Q_ARG(QString, vmin),
                              Q_ARG(QString, vmax));
}

3 个答案:

答案 0 :(得分:2)

这当然是可能的,但你需要小心。

实现这一目标的方法之一是:

  • 创建一个存储要使用的“当前”HSV值的对象
  • 将此对象的引用(或指针)提供给GUI线程和OpenCV线程
  • 当GUI想要“告诉”处理线程使用新值时,它会将它们发布到该对象
  • 当处理线程准备好在下一帧(循环体的开始)上移动时,它从该对象中获取值。

您只需确保使用互斥锁同步该共享对象上的set和get方法,以防止处理线程读取半写值(数据争用导致C ++中的未定义行为)。

答案 1 :(得分:1)

我认为这里最简单的解决方案可能是利用Qt信号/插槽跨线程工作的事实。

在处理线程中设置适当的插槽,然后从GUI线程发出信号。

关于您是否针对每个用户输入发出信号,或者您是否在GUI端暂时批量更改,还有各种有趣的问题......

文档中有一些关于线程同步的想法:http://doc.qt.io/qt-5/threads-synchronizing.html

答案 2 :(得分:1)

如果您在&#34;错误&#34;中使用QThread方式(通过继承QThread并使用:: run,与https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/比较),信号槽参数更改也在无限循环中起作用:

这是一个用于测试的小样本线程:

void MyThread::run()
{
    // start an infinite loop and test whether the sliderchange changes my used parameters

    std::cout << "start infinite loop" << std::endl;
    while(true)
    {
        unsigned long long bigVal = 0;
        int lastVal = mValue;

        std::cout << "start internal processing loop " << std::endl;
        for(unsigned long long i=0; i<1000000000; ++i)
        {
            bigVal += mValue;
            if(lastVal != mValue)
            {
                std::cout << "changed value: " << mValue << std::endl;
                lastVal = mValue;
            }
        }
        std::cout << "end internal processing loop: " << bigVal << std::endl;
    }
    std::cout << "stop infinite loop" << std::endl;
}

使用此SLOT,它连接到主窗口滑块SIGNAL

void MyThread::changeValue(int newVal)
{
    // change a paramter. This is a slot which will be called by a signal.

    // TODO: make this call thread-safe, e.g. by atomic operations, mutual exclusions, RW-Lock, producer-consumer etc...
    std::cout << "change value: " << newVal << std::endl;
    mValue = newVal;
}

使用滑块后给我这个结果:

enter image description here

这就是插槽的连接方式:

    QObject::connect(mSlider, SIGNAL(valueChanged(int)), mTestThread, SLOT(changeValue(int)) );

如果无限循环是作为某种workerObject方法执行的,它被移动到moveToThread的线程,你可以改变调用插槽的方式:

QObject::connect(mSlider, SIGNAL(valueChanged(int)), mTestThread, SLOT(changeValue(int)), Qt::DirectConnection );

从未使用过,但我想同样适用于调用:

QMetaObject::invokeMethod(hsv, "updateHsv", Qt::DirectConnection, ...

(主线程将调用changeValue然后工作线程不需要停止处理以更改值=&gt;值访问应该是线程安全的!

或者您必须处理该线程的事件队列:

while(true)
{
    [processing]
    QApplication::processEvents(); 
}