QT GUI freezes when capturing video from webcam using OpenCV - qt

I am using Opencv for some real-time video processing.
As a front-end I am using QT framework.
On my GUI, I have an input image window (mapped to a Label) and an output image window (mapped to another Label) and 3 push buttons. One to Start input video capture, the second to process the video (code not written yet), and third to Exit.
I am currently able to stream my video and display it on the Front-end. But this locks my GUI and am unable to Exit.
I tried using QTimers (using suggestions from this and the QT forum), but my GUI still remains locked.
Would appreciate if someone can point my in the right direction.
Below is the code:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp> // for cvtColor
#include <iostream>
#include <QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_buttonCaptureVideo_clicked();
void on_buttonExit_clicked();
public slots:
virtual void doNextFrame() {repaint();}
private:
Ui::MainWindow *ui;
CvCapture *capture; // OpenCV Video Capture Variable
IplImage *frame; // Variable to capture a frame of the input video
cv::Mat source_image; // Variable pointing to the same input frame
cv::Mat dest_image; // Variable to output a frame of the processed video
QTimer *imageTimer;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
cvReleaseImage(&frame);
cvReleaseCapture(&capture);
}
void MainWindow::on_buttonCaptureVideo_clicked()
{
// Set to 25 frames per second
const int imagePeriod = 1000/25; // ms
imageTimer = new QTimer(this);
imageTimer->setInterval(imagePeriod);
connect(imageTimer, SIGNAL(timeout()), this, SLOT(doNextFrame()));
// Use the default camera
capture = cvCreateCameraCapture(-1);
while(capture)
{
// Capture a frame
frame = cvQueryFrame(capture);
// Point to the same frame
source_image = frame;
// Resize Image
cv::resize(source_image, source_image, cv::Size(128,128) , 0, 0);
// Change to RGB format
cv::cvtColor(source_image,source_image,CV_BGR2RGB);
// Convert to QImage
QImage qimg = QImage((const unsigned char*) source_image.data, source_image.cols, source_image.rows, QImage::Format_RGB888); // convert to QImage
// Display on Input Label
ui->labelInputVideo->setPixmap(QPixmap::fromImage(qimg));
// Resize the label to fit the image
ui->labelInputVideo->resize(ui->labelInputVideo->pixmap()->size());
}
}
void MainWindow::on_buttonExit_clicked()
{
connect(ui->buttonExit, SIGNAL(clicked()), qApp, SLOT(closeAllWindows()));
}

When you click your button, the
while(capture) { ... }
loop will run forever, as capture will never be set to NULL.
This means the code flow never leaves your loop and thus the main thread cannot process anything else, like e.g. repainting.
The QTimer will emit its timeout() signals, but they will be placed as Events in Qt's Event Queue. As long as your on_buttonCaptureVideo_clicked() method is running, those Events will not be processed.
Here are my suggestions how to make it work:
This code:
// Set to 25 frames per second
const int imagePeriod = 1000/25; // ms
imageTimer = new QTimer(this);
imageTimer->setInterval(imagePeriod);
connect(imageTimer, SIGNAL(timeout()), this, SLOT(doNextFrame()));
// Use the default camera
capture = cvCreateCameraCapture(-1);
belongs into the constructor of MainWindow as you want to set that up one time only. There is no need to do it again when the user clicks the button the second, third, etc time.
The code which is within your while loop should go into the doNextFrame() slot (without the while construct).
Then your button will only do
imageTimer->start();
and then e.g. do
imageTimer->stop();
when it is clicked again.
Example code:
void MainWindow::on_buttonCaptureVideo_clicked()
{
if( imageTimer->isActive() )
{
imageTimer->stop();
}
else
{
imageTimer->start();
}
}
What will happen if you do that?
When you click the button, your on_buttonCaptureVideo_clicked() clicked slot will be called from the GUI thread, the timer will be started, and the method will return almost instantly.
Now the GUI thread is free and able to handle repaints etc.
From that point on, the timer will be sending timeout() signals every 40ms. Whenever the GUI thread is free, it will handle this signal and call your doNextFrame slot.
This slot will capture the next frame and return when it is done. When it is done, the GUI thread will be able to process other Events (e.g. repaint) again.
As soon as you click the button again, the timer will stop, and no new timeout() events will be sent. If you still see a couple of frames after the button has been clicked, this could mean that the timer events were sent faster than they could be processed.

Preface: I am not strong in C++ so I cannot offer specific code, but I am experienced in PyQt
This is a common pitfall for newcomers to Qt. What it seems your on_buttonCaptureVideo_clicked is doing is entering into a loop in your main GUI thread to do work. In QT, you want to avoid doing anything busy in your main thread. The Qt eventloop needs to be able to constantly process and flush your GUI events as they come in. What you are doing is blocking the event loop.
There are two different things you can do here. The first is the more basic approach but allows you to see more immediate results. You can "pump" the eventloop. Depending on how fast your while loop iterates, you can call qApp->processEvents();. This will allow Qt to process the pending GUI events and make your app appear more responsive. Its basically sharing time between your while loop and the main loop. Maybe you want to call this on every nth frame. Depends how often you want to make sure the GUI refreshes.
The other option, which is more preferable, is to place your capture loop into a QThread. When a new frame is available you can emit a signal with the frame data. The signal will get placed into the Qt event loop to be processed with everything else and will not hold up your GUI. Generally this is the approach you want to take with any heavy crunching or long running callables.
Edit
I just realized that you start a QTimer in addition to doing a loop in the main thread. If you want to use a QTimer, and your image processing is not too heavy (it doesn't take long per cycle) then you should move everything into the doNextFrame and completely remove the while loop. If your doNextFrame is a heavy process, then you should be using a QThread and signals.

Related

How may I fix my error prone on_foo_bar slots

I have eight list widgets in a tab widget. They have similar names, and Designer's "Go to slot" mechanism has made links to slots it names (in the "private slots" section of "mainwindow.h") like:
void on_SR0listWidget_itemClicked(QListWidgetItem *item);
I saw warnings that "Slots named on_foo_bar are error-prone," and now I need to change their names in order to discover if that's the cause of the weird behaviour I'm getting.
I tried simply refactoring the names, but that stopped the slot code from working. I used Designer's graphical "Edit Signal/Slot" mechanism and was able to connect a newly added list widget's "itemClicked(QListWidgetItem *item)" signal to a newly added slot, and it looks OK in the graphical representation, but there's no debug message (that I set up in the Slot function) when an item is clicked.
I also use those widgets' "entered" signals, so there will be at least 16 to fix. I would write a script if it could be done by parsing the relevant files.
One example of exactly how to rename one of my replacement slots and connect an "item clicked" or "entered" signal to it (and where it should go) would be a great help.
Signals/slots setup through the designer rely on the names of the widgets involved. This can lead to problems if the widget names are changed. There are times when using the designer method will lead to code that compiles but doesn't actually make the connections you expect. This is why you are getting that warning.
You can get more reliable behavior by connecting the signals and slots programmatically. For example, let's say you have a class header such as:
#include <QMainWindow>
namespace Ui {
class MyWindow;
};
class QListWidgetItem;
class MyWindow : public QMainWindow {
Q_OBJECT
public:
explicit MyWindow(QWidget* parent = nullptr);
~MyWindow() override;
private:
void handleItemClicked(QListWidgetItem* item); // this is your slot
Ui::MyWindow* ui;
};
You can connect the signal/slot together in the cpp file like this:
#include "MyWindow.h"
#include "ui_MyWindow.h"
#include <QDebug>
MyWindow::MyWindow(QWidget* parent)
: QWidget(parent),
ui(new Ui::MyWindow()) {
ui->setupUi(this);
// connect() has many overloads, but in this case we are passing:
// 1. the object emitting the signal
// 2. the signal being emitted
// 3. the object whose slot we want to call
// 4. the slot to connect to
connect(
ui->listWidget, &QListWidget::itemClicked,
this, &MyWindow::handleItemClicked);
}
MyWindow::~MyWindow() {
delete ui;
}
void MyWindow::handleItemClicked(QListWidgetItem* item) {
qDebug() << "item clicked";
}
You can still use the designer to layout your UI - but prefer to manage connections directly in code rather than through the designer.

Strange undocumented QTimer/QEventLoop behaviour after the timer is manually restarted

I have recently stumbled upon this while working with QTimer that calls a function with internal QEventLoop
So, say we have a QTimer instance
QTimer* timer = new QTimer;
somewhere in the constructor we start it and it begins ticking away once every 100ms
timer->start(100);
now the fun part, we connect it to a slot that has internal QEventLoop
void SlotFunction()
{
qDebug() << "entered";
QEventLoop loop;
loop.exec();
}
putting aside how stupid this loop really is, we see that we will never finish processing the slot and timer's subsequent timeouts will keep stacking into execution queue. Everything is ok and as it should be.
What is NOT as it should be comes next: since QEventLoop makes sure our app stays responsive while the slot mindlessly idles away we can make a button and its clicked() slot that looks like:
void OnClicked()
{
timer->start(100);
}
what I am doing here is essentially restarting current timer cycle, nothing less, nothing more. Right? Nope! After this restart, SlotFunction fires again suggesting that tick after timer's restart is not in fact equal to all other ticks that were issued before it...
My only question is : WTF?! Why manually restarting the timer enables it to enter the slot additional time? I've asked on freenode but the only answer I got was "It is as it should be"
I tried this and every click creates another "entered" line.
The main Eventloop cannot handle another event since we are stuck in a new eventloop.
This is quite easy to see when implementing a second slot and also connecting this slot to the timeout signal.
The maineventloop will get stuck when calling the next eventloop and not processing any more queued events.
The timer itself will also not queue any more events, since the queueing up itself would be done in the now stuck main-eventloop. The timer does not run in its own eventloop (thats why Qtimers are no precision timers).
As soon as the button is clicked the new eventloop checks the timer if an event timeout() should be generated.
As soon as the new event is handled we again are stuck in another eventloop...
This will go on until we exit the application.
When exiting the application we see the loops reversing and calling the second slot as often as we clicked the button and ran into the first slot
Code:
#include <QDebug>
#include <QTime>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
timer = new QTimer;
timer->setInterval(2000);
connect(timer,SIGNAL(timeout()),this,SLOT(timerslot()));
connect(timer,SIGNAL(timeout()),this,SLOT(timerslot2()));
timer->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
timer->start(2000);
}
void MainWindow::timerslot()
{
qDebug()<<"In";
QEventLoop loop;
loop.exec();
}
void MainWindow::timerslot2()
{
qDebug()<<"More";
}
Output on start:
In
Output on every click:
In
Output after 3 clicks:
In
In
In
In
Output exiting the application:
In
In
In
In
More
More
More
More

displaying QSlider value in QLabel/QDebug() using QThread

I am implementing a simple function where slider value is constantly displayed on label and qDebug(). I already got the label updated using signal/slots, but somehow the qDebug() thread is not working properly. I expected to see the console flooded with the value of the slider.
Below is my code:
SliderThread.h:
class HorizontalSliderThread : public QThread {
Q_OBJECT
public:
HorizontalSliderThread(Ui::MainWindow *ui);//in order to call slider value in HorizontalSliderThread class
~HorizontalSliderThread();
public slots:
void process();
private:
};
SliderThread.cpp
HorizontalSliderThread::HorizontalSliderThread(Ui::MainWindow *ui){
ui_global = *ui;
}
void HorizontalSliderThread::process(){
qDebug("Test Thread");
int value = ui_global.horizontalSlider_windowSize->value();
QObject::connect(ui_global.horizontalSlider_windowSize,SIGNAL(valueChanged(int)),ui_global.label_SliderWindowSize,SLOT(setNum(int)));//update value to label
qDebug()<<value; //update value in console
}
mainwindow.h
move Ui::MainWindow *ui; from private to public.
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QThread* thread = new QThread;
HorizontalSliderThread* slider = new HorizontalSliderThread(ui);
slider->moveToThread(thread);
connect(thread, SIGNAL(started()), slider, SLOT(process()));
thread->start();
}
Current Output: qDebug() displays the value of slider once, label is updated constantly.
Expected Output: qDebug() displays the value of slider continuously, label is updated constantly.
Since label is updated when the slider is moved, then the signal/slot for this function is working, which means my thread should be working. Don't know what I'm doing wrong.
QThread implementation is reference from: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
I am fairly new to this, especially QThread, so if there is a better way to implement this function, please let me know!
Thanks a lot.
EDIT1: add slider->moveToThread(thread);
Well you've only connected the started() signal to your process() function, so your thread starts and it calls process, which dutifully shows your debug output and returns.
I suspect you want to call your process function every time the value changes, which would require another signal/slot connection (along these lines):
connect(ui_global.horizontalSlider_windowSize, SIGNAL(valueChanged(int)), thread, SLOT(setNum(int)));
Also, it looks like you intend for your HorizontalSliderThread to actually run in that separate thread you've created, in which case you'll need a call to QObject::moveToThread() in there, something along these lines:
slider->moveTothread(thread);

QMessageBox halts QTimer

I created a QMessageBox instance with single button "OK" in slot connected to QTimer signal timeout() and displayed it using exec function and it seems that timer halts until button pressed and the box closed. I expected exec to create local event loop and dispatching timer messages, nevertheless timer was halted (no signal timeout() emited). Can anyone explain? P.S Sorry for my english.
UPDATE:
sample code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QTimer *tm1=new QTimer(this),
*tm2=new QTimer(this);
connect(tm1,SIGNAL(timeout()),this,SLOT(tslot1()));
connect(tm2,SIGNAL(timeout()),this,SLOT(tslot2()));
tm1->start(1000);
tm2->start(1000);
}
void MainWindow::tslot1(void)
{
static int cnt;
qWarning(QString("slot 1 called %1 time(s)").arg(++cnt).toAscii().data());
}
void MainWindow::tslot2(void)
{
static int cnt;
qWarning(QString("slot 2 called %1 time(s)").arg(++cnt).toAscii().data());
if (3==cnt)
{
QMessageBox *mb=new QMessageBox(QMessageBox::Critical,tr("Error"),tr("tm2 halted !"),QMessageBox::Ok,this);
mb->exec();
}
}
MainWindow::~MainWindow()
{
delete ui;
}
Well, you can see that event loop is running, because timer1 is firing timeouts. From documentation you can see that exec() is blocking call - so it will block the caller function (QTimer slot) until dialog is closed.
That leads us to the QObject source code. After executing slot function, in QMetaObject::activate, last step is to reset the sender of the signal:
QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);
This will reset QTimer state, and it will start countdown again. I think this is the common-sense approach to ensure that timer callbacks are called sequentially - like timers in most UI libraries, and not in parallel within the same timer.
Because exec() will block timer callback, QTimer will not be reset and callback will not be called again (until function resumes, and QTimer gets its reset).
You have several ways in handling this:
1) If you don't want your function to block, and you don't need result of modal dialog to proceed with the function - call QMessageBox::open() instead of QMessageBox::exec() - it will not block the caller function.
2) If you need result of the dialog to continue the function, and you don't want to stop your timer callbacks, naive approach is to reset QTimer manually by calling QTimer::stop() and QTimer::start() sequentially when entering the callback function.

Passing signals through hierarchies

I am having some difficulty fully grasping how signals and slots are used in Qt. I am sure it is really basic but I'm just no getting it today.
I have a set of widgets a bit like this:
MainWindow
-->StackedWidget
-->ChildForms
Now the idea is that there are some actions on the Child widgets that will cause the stacked widget to display a different page.
So if I understand it properly I thought the way to connect signals and slots is to use the connect() at the scope that knows about the objects but what I have managed to get working doesn't do it this way. At the moment in my child form I use parentWidget() to access the slot of the StackedWidget but I am not very happy with really because it is giving the child information about the parent which it shouldn't have:
void TaskSelectionForm::setButtonMappings()
{
// Set up a mapping between the buttons and the pages
QSignalMapper *mapper = new QSignalMapper(this);
connect(mapper, SIGNAL(mapped(int)), parentWidget(), SLOT(setCurrentIndex(int)));
mapper->setMapping(ui->utilitiesButton, 2); // Value of the index
connect(ui->utilitiesButton, SIGNAL(clicked()), mapper, SLOT(map()));
}
But I am not really sure how I should do this and connect it up. Do I need to have signals at each level and emit through the tree?
A Bit of Signal-Slot Theory
The signal-slot connections are oblivious to parent-child relationships between QObjects, and any such relationship doesn't matter. You're free to connect objects to their children, to their siblings, to their parents, or even to QObjects that are in a separate hierarchy, or to lone QObjects that have neither parents nor children. It doesn't matter.
A signal-slot connection connects a signal on a particular instance of QObject to slot on another instance of QObject. To use the connect method, you need the pointers to the instance of sender QObject and the instance of receiver QObject. You then use the static QObject::connect(sender, SIGNAL(...), receiver, SLOT(...)). Those connections have nothing to do with any hierarchy there is between the sender and receiver.
You can also connect a signal to a signal, to forward it -- for example from a private UI element to a signal that's part of the API of the class. You cannot connect a slot to a slot, because it'd incur a bit of runtime overhead for a rarely-used case. The overhead would be an extra bool member in QObjectPrivate, plus a failed if (bool) test. If you want to forward slots to slots, there are at least two ways to do it:
Emit a signal in the source slot and connect that signal to the destination slot.
Obtain a list of all signals connected to the source slot, iterate on it and connect them to to the target slot. There's no easy way to maintain such connections when further signals are connected or disconnected from the source slot. Unfortunately, QObject only has a connectNotify(const char*) protected function, but not a signal -- so you can't hook up to it unless you would modify src/corelib/kernel/qobject[.cpp,_p.h,.h] to emit such a signal. If you truly need it, just modify the Qt source, you have access it for a reason, after all. Hacking the vtable without modifying Qt is possible, but discouraged for obvious reasons.
The Answer
Below is a self contained example that shows how to do what you want. Turns out I have answers to quite a few questions from my various experiments I've done in Qt in the past. I'm a packrat when it comes to test code. It's all SSCCE to boot :)
// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-slot-hierarchy-10783656
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
class Window : public QWidget
{
QSignalMapper m_mapper;
QStackedLayout m_stack{this};
QWidget m_page1, m_page2;
QHBoxLayout m_layout1{&m_page1}, m_layout2{&m_page2};
QLabel m_label1{"Page 1"}, m_label2{"Page 2"};
QPushButton m_button1{"Show Page 2"}, m_button2{"Show Page 1"};
public:
Window(QWidget * parent = {}) : QWidget(parent) {
// the mapper tells the stack which page to switch to
connect(&m_mapper, SIGNAL(mapped(int)), &m_stack, SLOT(setCurrentIndex(int)));
// Page 1
m_layout1.addWidget(&m_label1);
m_layout1.addWidget(&m_button1);
// tell the mapper to map signals coming from this button to integer 1 (index of page 2)
m_mapper.setMapping(&m_button1, 1);
// when the button is clicked, the mapper will do its mapping and emit the mapped() signal
connect(&m_button1, SIGNAL(clicked()), &m_mapper, SLOT(map()));
m_stack.addWidget(&m_page1);
// Page 2
m_layout2.addWidget(&m_label2);
m_layout2.addWidget(&m_button2);
// tell the mapper to map signals coming from this button to integer 0 (index of page 1)
m_mapper.setMapping(&m_button2, 0);
connect(&m_button2, SIGNAL(clicked()), &m_mapper, SLOT(map()));
m_stack.addWidget(&m_page2);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
Connect(stackedwidget->currentactivewidget,SIGNAL(OnAction()),this,SLOT(PrivateSlot()));
PrivateSlot() is a slot declared privately. So in this function, you can add your code to change the page of stackedwidget corresponding to the action produced by currentactivewidget.
Again if you really want to pass the signal up the heirarchy, emit a publicsignal() at the end of private slot function.
Connect(this,SIGNAL(publicsignal()),Parentwidgetofstackedwidget(here mainwindow),SLOT(mainwindow_slot()));

Resources