This post shows a form control widget that performs validation on its input. The invalid input is highlighted in red and the error message is display below the input. The input is validated in real-time (as the text is changed) when invalid the submit button is disabled and when valid it is enabled. The desired effect is shown below:
Showing posts with label QT. Show all posts
Showing posts with label QT. Show all posts
Saturday, 16 June 2018
Saturday, 9 June 2018
Order Independent Transparency
Transparency in OpenGL is usually achieved using blending. Good looking results can be achieved by changing the blend function used and in the process we can avoid advanced techniques that require depth sorting.
In this post, I show how to achieve order independent transparency with python and OpenGL. The desired effect is shown below:
Saturday, 2 June 2018
Styling QT Applications: Button Tabs
This post demonstrates how to create custom tabs using QPushButtons in a QButtonGroup. The code below is self explanatory, the button group ensures that only a single button can be selected at one time. The buttons are made to look more like tabs using QT style sheet and the final result is shown below
Friday, 25 May 2018
Add checkbox in QTableView header using icons
There is no API to insert a checkbox into the header of a QTableView. The recommended way of achieving this is to subclass the QHeaderView and draw the checkbox in the paintSection() method which is in my opinion overkill for such a simple feature.
In this post, I show how to achieve a similar effect by simply adding icons in the header. The desired effect is shown below:
Monday, 27 May 2013
Working with Video Using OpenCV and QT - Part 2
This tutorial was written because of a request from a previous tutorial. In this tutorial, we improve upon the work done in that tutorial by adding a track-bar and display duration of the video. Also a few errors, I discovered will also be corrected in this tutorial. So let get cracking.
Add widgets to GUI
This is an extension to a previous tutorial so I will only point out changes or additions to the previous work so as not to repeat myself. Open the mainwindow.ui file, this file
could be edited manually but for this tutorial we would use the
designer.
- Add a horizontal track-bar to the GUI, this would be used to the adjust the position in the video.
- Add two labels to the GUI; place one on the left and another on the right of the horizontal track-bar ( or however suits your needs) the left one would show the current time into the video whereas the right one would be used to show the total time of the video. The GUI should now look similar to the image above.
Player Class Definition
Now we add a few function definitions to Player class header file -player.h.
#ifndef PLAYER_H #ifndef PLAYER_H #define PLAYER_H #include <QMutex> #include <QThread> #include <QImage> #include <QWaitCondition> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; class Player : public QThread { Q_OBJECT private: .... VideoCapture *capture; Mat RGBframe; QImage img; signals: ...... protected: ...... public: ....... //set video properties void setCurrentFrame( int frameNumber); //Get video properties double getFrameRate(); double getCurrentFrame(); double getNumberOfFrames(); }; #endif // VIDEOPLAYER_H
The class definition is still simple and straightforward. We add a few public setter and getter functions to enable us grab some important video parameters. Also I change the capture variable to a pointer I discovered that it was not possible to reload a new video once one has been loaded so to correct this I initialize a new VideoCapture instance when a new video is loaded. So the method used to access the VideoCapture instance members must be changed from the "." to the "->" notation.
Player Class Implementation
Here is the constructor for the Player class.
bool Player::loadVideo(string filename) { capture = new cv::VideoCapture(filename); if (capture->isOpened()) { frameRate = (int) capture->get(CV_CAP_PROP_FPS); return true; } else return false; } void Player::run() { int delay = (1000/frameRate); while(!stop){ if (!capture->read(frame)) { stop = true; } ..... } }
In the loadVideo() method, we use the instance of the VideoCapture class to load the video and set the frame rate. As you should already know the VideoCapture class is from the OpenCV library
double Player::getCurrentFrame(){ return capture->get(CV_CAP_PROP_POS_FRAMES); } double Player::getNumberOfFrames(){ return capture->get(CV_CAP_PROP_FRAME_COUNT); } double Player::getFrameRate(){ return frameRate; } void Player::setCurrentFrame( int frameNumber ) { capture->set(CV_CAP_PROP_POS_FRAMES, frameNumber); }
Here are the getter and setter function to access the video information. I ran into a glitch, with ffmpeg where the capture->get(CV_CAP_PROP_FRAME_COUNT) function returned the wrong frame count. The solution might be to update my ffmpeg. I would update the post once the solution is confirmed.
Player::~Player() { mutex.lock(); stop = true; capture.release(); delete capture; condition.wakeOne(); mutex.unlock(); wait(); }
Here is the rest of the Player class, in the destructor we release the VideoCapture object, also we allocated memory with the new keyword it must be freed.
MainWindow Class Definition
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QFileDialog> #include <QMessageBox> #include <player.h> #include <QTime> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: ....... private slots: ......... QString getFormattedTime(int timeInSeconds); void on_horizontalSlider_sliderPressed(); void on_horizontalSlider_sliderReleased(); void on_horizontalSlider_sliderMoved(int position); private: ........ }; #endif // MAINWINDOW_H
Here is the class definition for the MainWindow class, we include the 3 event slots for the new horizontal slider and a update some other functions
Mainwindow class implementation
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ....... ui->pushButton_2->setEnabled(false); ui->horizontalSlider->setEnabled(false); }
We disable the play button and the horizontal slider; these would be enabled once a video is loaded.
void MainWindow::updatePlayerUI(QImage img) { if (!img.isNull()) { ui->label->setAlignment(Qt::AlignCenter); ui->label->setPixmap(QPixmap::fromImage(img).scaled(ui->label->size(), Qt::KeepAspectRatio, Qt::FastTransformation)); ui->horizontalSlider->setValue(myPlayer->getCurrentFrame()); ui->label_2->setText( getFormattedTime( (int)myPlayer->getCurrentFrame()/(int)myPlayer->getFrameRate()) ); } }
The updatePlayerUI slot receives a QImage and resizes it to fit the label (keeping the aspect ratio) which will be used to display. It displays the image by setting the label pixmap. We also update the horizontal slider position and the label that displays the elapsed time. We calculate the duration of the video but dividing the total number of frames by the frame rate.
void MainWindow::on_pushButton_clicked() { QString filename = QFileDialog::getOpenFileName(this, tr("Open Video"), ".", tr("Video Files (*.avi *.mpg *.mp4)")); QFileInfo name = filename; if (!filename.isEmpty()){ if (!myPlayer->loadVideo(filename.toAscii().data())) { QMessageBox msgBox; msgBox.setText("The selected video could not be opened!"); msgBox.exec(); } else{ this->setWindowTitle(name.fileName()); ui->pushButton_2->setEnabled(true); ui->horizontalSlider->setEnabled(true); ui->horizontalSlider->setMaximum(myPlayer->getNumberOfFrames()); ui->label_3->setText( getFormattedTime( (int)myPlayer->getNumberOfFrames()/(int)myPlayer->getFrameRate()) ); } } } QString MainWindow::getFormattedTime(int timeInSeconds){ int seconds = (int) (timeInSeconds) % 60 ; int minutes = (int) ((timeInSeconds / 60) % 60); int hours = (int) ((timeInSeconds / (60*60)) % 24); QTime t(hours, minutes, seconds); if (hours == 0 ) return t.toString("mm:ss"); else return t.toString("h:mm:ss"); } void MainWindow::on_horizontalSlider_sliderPressed() { myPlayer->Stop(); } void MainWindow::on_horizontalSlider_sliderReleased() { myPlayer->Play(); } void MainWindow::on_horizontalSlider_sliderMoved(int position) { myPlayer->setCurrentFrame(position); ui->label_2->setText( getFormattedTime( position/(int)myPlayer->getFrameRate()) ); }
This is the remaining part of the MainWindow Class, the getFormattedTime function takes the time in seconds and formats it for display, the rest is self explanatory. You can Download the full code Here.
Final words...
This is just a simple tutorial to help anyone get started with videos in OpenCV and QT. Please let me know if this was helpful and ask questions and give suggestions(if any) in the comments. Happy Coding!
Tuesday, 12 March 2013
Plot Multi-Channel Histogram in QWT -Part 2
This is the second part in a series on how to do multi-channel plots in QWT. These kinds of plots can be done very simply in Matlab but are more challenging to implement in C++. Armed with the QWT library, we have investigated how to overlay multiple channel histograms on each other. Here I will show you how to make a plot similar to what you would do with the subplot() function in Matlab. This method simply puts multiple channels into one window in a grid form; this method is more presentable and makes it easier to compare plots. Lets get started...
Code
The most important part of the code is the PlotMatrix class which uses a QGridLayout to arrange the sub-plots into a matrix with specified number of rows and cols.The class is defined in the plotmatix.h header file and implemented in plotmatrix.cpp. In main.cpp we define and implement a MainWindow class which inherits from the PlotMatrix class, the histograms are created from an image and displayed in the MainWindow.
The Main function and others
The MainWindow class is a derived class from the PlotMatrix class. Here, we define a 2 by 3 matrix of plots in a static manner(Although, we only want to plot 3 channels). Grid lines are included in the plots for fun but this can be removed or edited as needed.
#include <QApplication> #include <plotmatrix.h> #include <qwt_plot.h> #include <qwt_plot_curve.h> #include <qwt_plot_grid.h> #include <qwt_symbol.h> #include <qwt_legend.h> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; class MainWindow: public PlotMatrix { public: MainWindow():PlotMatrix( 2, 3 ) { for ( int row = 0; row < numRows(); row++ ) { for ( int col = 0; col < numColumns(); col++ ) { //set default scale if any. setAxisScale( QwtPlot::yLeft, row, col, 0, 100 ); setAxisScale( QwtPlot::xBottom, row, col, 0, 255 ); QwtPlot *plt = plot( row, col ); plt->setCanvasBackground( QColor( Qt::darkGray ) ); //Allows the Axis to scale automatically. plt->setAxisAutoScale(QwtPlot::yLeft, true); //Show plot grid lines just for aesthetics. QwtPlotGrid *grid = new QwtPlotGrid(); grid->enableXMin( true ); grid->setMajorPen( Qt::white, 0, Qt::DashLine); grid->setMinorPen( Qt::gray, 0 , Qt::DotLine ); grid->attach( plt ); } } } }; void getPoints(MatND& hist, int* histSize, QPolygonF& points) { points.clear(); for( int h = 0; h < histSize[0]; ++h) { float bin_value = hist.at<float>(h); points << QPointF((float)h, bin_value); } } class Curve: public QwtPlotCurve { public: Curve( const QString &title, const QColor &colour, int penSize, QPolygonF &points ):QwtPlotCurve( title ) { QColor c = colour; c.setAlpha( 150 );//handle colour transparency setPen(c, penSize); setBrush( c ); setSamples( points ); setRenderHint( QwtPlotItem::RenderAntialiased ); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); if (argc < 2) return 1; //Read input image Mat img = imread(argv[1]); //Convert to grayscale if (img.data && img.channels() != 3) return 1; MainWindow mainWindow; mainWindow.resize( 600, 400 ); int histSize[] = {256}; // number of bins float hranges[] = {0.0, 255.0}; // min and max pixel value const float* ranges[] = {hranges}; int channels[] = {0}; // only 1 channel used std::vector<Mat> rgbChannels(3); split(img, rgbChannels); MatND hist; QPolygonF points; calcHist(&rgbChannels[2], 1, channels, Mat(), hist, 1, histSize,ranges); /*Insert the points that should be plotted on the graph in a Vector of QPoints or a QPolgonF */ getPoints(hist, histSize, points); Curve *curve = new Curve("Red Channel", Qt::red, 2, points); curve->attach( mainWindow.plot(0,0) ); // Attach curve to the plot calcHist(&rgbChannels[1], 1, channels, Mat(), hist, 1, histSize,ranges); getPoints(hist, histSize, points); curve = new Curve("Green Channel", Qt::green, 2, points); curve->attach( mainWindow.plot(0,1) ); calcHist(&rgbChannels[0], 1, channels, Mat(), hist, 1, histSize,ranges); getPoints(hist, histSize, points); curve = new Curve("Blue Channel", Qt::blue, 2, points); curve->attach( mainWindow.plot(0,2) ); mainWindow.show(); //Show plot return a.exec(); }
PlotMatrix class
This implementation uses a QVector of pointers to QwtPlot instances to represent the matrix of subplots. It provides a function plot(row, col) which returns the pointer to the desired plot.
//PlotMatrix.h #ifndef _PLOT_MATRIX_H_ #define _PLOT_MATRIX_H_ #include <qframe.h> #include <qwt_plot.h> class PlotMatrix: public QFrame { Q_OBJECT public: PlotMatrix( int rows, int columns, QWidget * parent = NULL ); virtual ~PlotMatrix(); int numRows() const; int numColumns() const; QwtPlot* plot( int row, int column ); const QwtPlot* plot( int row, int column ) const; void setAxisScale(int axisId, int row, int col, double min, double max, double step = 0 ); private: QVector<QwtPlot *> plotWidgets; }; #endif //PlotMatrix.cpp //This is based on the PlotMatrix implementation in the Qwt Samples. #include <qlayout.h> #include <qpen.h> #include <qwt_plot.h> #include <qwt_scale_widget.h> #include <qwt_scale_draw.h> #include "plotmatrix.h" PlotMatrix::PlotMatrix( int numRows, int numColumns, QWidget *parent ): QFrame( parent ) { plotWidgets.resize( numRows * numColumns ); QGridLayout *layout = new QGridLayout( this ); for ( int row = 0; row < numRows; row++ ) { for ( int col = 0; col < numColumns; col++ ) { QwtPlot *plot = new QwtPlot( this ); layout->addWidget( plot, row, col ); plotWidgets[row * numColumns + col] = plot; } } } PlotMatrix::~PlotMatrix() { foreach(QwtPlot* p, plotWidgets) delete p; } int PlotMatrix::numRows() const { const QGridLayout *l = qobject_cast<const QGridLayout *>( layout() ); if ( l ) return l->rowCount(); return 0; } int PlotMatrix::numColumns() const { const QGridLayout *l = qobject_cast<const QGridLayout *>( layout() ); if ( l ) return l->columnCount(); return 0; } QwtPlot* PlotMatrix::plot( int row, int column ) { const int index = row * numColumns() + column; if ( index < plotWidgets.size() ) return plotWidgets[index]; return NULL; } const QwtPlot* PlotMatrix::plot( int row, int column ) const { const int index = row * numColumns() + column; if ( index <plotWidgets.size() ) return plotWidgets[index]; return NULL; } void PlotMatrix::setAxisScale( int axis, int row, int col, double min, double max, double step ) { QwtPlot *plt = plot( row, col ); if ( plt ) { plt->setAxisScale( axis, min, max, step ); plt->updateAxes(); } }
Result
Conclusion
The subplot() is one of the most useful matlab functions if you plot several graphs or images. Now you can have something similar in QWT although not as simple. It is also possible to plot multi-channel histograms in a 3 dimensional plot, we leave this for another time. Happy Coding!.
Saturday, 2 March 2013
Plot Multi-Channel Histogram in QWT -Part 1
Here I will show you how to plot a multi-channel/colour histogram using QWT. I generate the histograms for the different channels in a RGB image using OpenCV, then I plot the histogram of each channel overlaid on each other on the same axis. This method is suitable for visualization but not very suitable for peak/valley histogram analysis as the overlaid colours would merge making it difficult to see what peaks/valleys belong to which channel.
The set-up for this tutorial is similar to the one in my previous tutorial, only this time I also include the OpenCV library. Again, we can just dive straight into the code.
Code
#include <QApplication> #include <qwt_plot.h> #include <qwt_plot_curve.h> #include <qwt_plot_grid.h> #include <qwt_symbol.h> #include <qwt_legend.h> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; void getPoints(MatND& hist, int* histSize, QPolygonF& points) { for( int h = 0; h < histSize[0]; ++h) { float bin_value = hist.at<float>(h); points << QPointF((float)h, bin_value); } } class Curve: public QwtPlotCurve { public: Curve( const QString &title ): QwtPlotCurve( title ) { setRenderHint( QwtPlotItem::RenderAntialiased ); } void setColour(const QColor &color, int penSize) { QColor c = color; c.setAlpha( 150 ); setPen(c, penSize); setBrush( c ); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); if (argc < 2) return 1; //Read input image Mat img = cv::imread(argv[1]); //Convert to grayscale if (img.data && img.channels() != 3) return 1; QwtPlot plot; //Create plot widget plot.setTitle( "Plot Demo" ); //Name the plot plot.setCanvasBackground(Qt::white ); //Set the Background colour plot.setAxisScale(QwtPlot::xBottom,0,255); //Scale the x-axis plot.insertLegend(new QwtLegend()); //Insert a legend int histSize[] = {256}; // number of bins float hranges[] = {0.0, 255.0}; // min and max pixel value const float* ranges[] = {hranges}; int channels[] = {0}; // only 1 channel used std::vector<cv::Mat> rgbChannels(3); split(img, rgbChannels); MatND hist; QPolygonF points; calcHist(&rgbChannels[2], 1, channels, cv::Mat(), hist, 1, histSize,ranges); Curve *curve = new Curve("Red Channel"); curve->setColour(Qt::red , 2);//Set colour and thickness for drawn curve. /*Insert the points that should be plotted on the graph in a Vector of QPoints or a QPolgonF */ getPoints(hist, histSize, points); curve->setZ( curve->z() - 1 ); curve->setSamples(points); //pass points to be drawn on the curve curve->attach( &plot ); // Attach curve to the plot calcHist(&rgbChannels[1], 1, channels, cv::Mat(), hist, 1, histSize,ranges); curve = new Curve("Green Channel"); curve->setColour(Qt::green , 2); points.clear(); getPoints(hist, histSize, points); curve->setZ( curve->z() - 2 ); curve->setSamples( points ); curve->attach( &plot ); calcHist(&rgbChannels[0], 1, channels, cv::Mat(), hist, 1, histSize,ranges); curve = new Curve("Blue Channel"); curve->setColour(Qt::blue, 2); points.clear(); getPoints(hist, histSize, points); curve->setZ( curve->z() - 3 ); curve->setSamples( points ); curve->attach( &plot ); plot.resize( 600, 400 ); //Resize the plot plot.show(); //Show plot return a.exec(); }
Result
Figure 1: A) The input image B) The resulting plot
Note: It is possible to remove the coloured area under the curves and reduce the plot to a simple line plot. This would make each channels' peaks and valleys more visible. This could be done by commenting out line:36 in the code. The result is shown below.
Figure 2: The result with the setBrush() property turned off.
Conclusion
That concludes this part of the tutorial, it is also possible to plot each histogram in its separate axis (in a matrix format) similar to what using the subplot() function in matlab does. This would be seen in our next tutorial. Happy Coding!.
Saturday, 2 February 2013
Draw an OpenCV histogram using QWT
The QWT Library gives us the ability to create graphs, scale axes, insert legend and do a whole lot of graphing stuff, in a very easy manner. I wanted to show how easy it was to use so in this tutorial I plot an openCV histogram using QWT.
The set-up for this tutorial is similar to the one in my previous tutorial, only this time I also include the OpenCV library. So we can dive straight into the code.
Code
#include <QApplication> #include <qwt_plot.h> #include <qwt_plot_curve.h> #include <qwt_plot_grid.h> #include <qwt_symbol.h> #include <qwt_legend.h> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> int main(int argc, char *argv[]) { QApplication a(argc, argv); if (argc < 2) return 1; //Read input image cv::Mat img = cv::imread(argv[1]); //Convert to grayscale if (img.data && img.channels() == 3) cv::cvtColor(img, img, CV_BGR2GRAY); else return 1; int histSize[1] = {256}; // number of bins float hranges[2] = {0.0, 255.0}; // min and max pixel value const float* ranges[1] = {hranges}; int channels[1] = {0}; // only 1 channel used cv::MatND hist; // Compute histogram cv::calcHist(&img, 1, channels, cv::Mat(), hist, 1, histSize,ranges); double minVal, maxVal; cv::minMaxLoc(hist, &minVal, &maxVal);//Locate max and min values QwtPlot plot; //Create plot widget plot.setTitle( "Plot Demo" ); //Name the plot plot.setCanvasBackground( Qt::black ); //Set the Background colour plot.setAxisScale( QwtPlot::yLeft, minVal, maxVal ); //Scale the y-axis plot.setAxisScale(QwtPlot::xBottom,0,255); //Scale the x-axis plot.insertLegend(new QwtLegend()); //Insert a legend QwtPlotCurve *curve = new QwtPlotCurve(); // Create a curve curve->setTitle("Count"); //Name the curve curve->setPen( Qt::white, 2);//Set colour and thickness for drawing the curve //Use Antialiasing to improve plot render quality curve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); /*Insert the points that should be plotted on the graph in a Vector of QPoints or a QPolgonF */ QPolygonF points; for( int h = 0; h < histSize[0]; ++h) { float bin_value = hist.at<float>(h); points << QPointF((float)h, bin_value); } curve->setSamples( points ); //pass points to be drawn on the curve curve->attach( &plot ); // Attach curve to the plot plot.resize( 600, 400 ); //Resize the plot plot.show(); //Show plot return a.exec(); }
The code is well commented and is therefore self explanatory - no need for extra explanations. The result can be seen in the image below.
Wednesday, 30 January 2013
Getting started with QWT
I have been looking for a graphing API for QT and eventually found one that suits my needs called QWT ( Qt Widgets for Technical Applications). I ran into some issues while setting it up but finally got it running so here is the resulting tutorial on how to set-up QWT in QT creator (for Linux 12.0.4).
Now we can begin, first step is to download the source files from here. The version used for this tutorial is QWT 6.1. In the terminal, navigate to the location of the downloaded .tar.bz2 file, then type the following commands:
$ tar -xjvf qwt-6.1-rc3.tar.bz2 $ cd qwt-6.1-rc3 $ qmake qwt.pro $ make $ make install
Launch QT creator and then Go to File > New File or Project. Select a QT Gui Application give the project a name e.g. “FirstQwtProject”.You could leave the other settings in the wizard as they are or change them to suit your own projects. I have kept the 'FirstQwtProject.pro' ( this will vary depending on the Project Name you have chosen) and 'main.cpp' but deleted the remaining files (MainWindow class) as they would not be need for this simple introduction. To run QWT programs in QT creator we need to let the IDE know where to find the QWT libraries.
So open the .pro file associated with the project and append the following lines at the end of the file. This should be included in every QWT Project you create but remember to change the include Path "/usr/local/qwt-6.1.0-rc3/.." to the location of the QWT install directory on your PC.
CONFIG += qwt INCLUDEPATH +="/usr/local/qwt-6.1.0-rc3/include" LIBS += -L/usr/local/qwt-6.1.0-rc3/lib -lqwt
Now go to the 'main.cpp' file and type in the following lines of code and press Ctrl+R to run it.
#include <QApplication> #include <qwt_plot.h> #include <qwt_plot_curve.h> #include <qwt_plot_grid.h> #include <qwt_symbol.h> #include <qwt_legend.h> int main(int argc, char *argv[]) { QApplication a(argc, argv); QwtPlot plot; plot.setTitle( "Plot Demo" ); plot.setCanvasBackground( Qt::white ); plot.setAxisScale( QwtPlot::yLeft, 0.0, 10.0); plot.insertLegend( new QwtLegend() ); QwtPlotGrid *grid = new QwtPlotGrid(); grid->attach( &plot ); QwtPlotCurve *curve = new QwtPlotCurve(); curve->setTitle( "Pixel Count" ); curve->setPen( Qt::blue, 4 ), curve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); QwtSymbol *symbol = new QwtSymbol( QwtSymbol::Ellipse, QBrush( Qt::yellow ), QPen( Qt::red, 2 ), QSize( 8, 8 ) ); curve->setSymbol( symbol ); QPolygonF points; points << QPointF( 0.0, 4.4 ) << QPointF( 1.0, 3.0 ) << QPointF( 2.0, 4.5 ) << QPointF( 3.0, 6.8 ) << QPointF( 4.0, 7.9 ) << QPointF( 5.0, 7.1 ); curve->setSamples( points ); curve->attach( &plot ); plot.resize( 600, 400 ); plot.show(); return a.exec(); }
This is what you should see.
Conclusion
QWT Libraries are one of many options for plotting graphs in QT. In the next tutorial, I will show you how to plot histograms calculated via OpenCV with QWT.
Monday, 10 December 2012
Working with Video Using OpenCV and QT
Video processing is a very important task in computer vision applications. OpenCV comes with its own GUI library (Highgui); but this library has no support for buttons and some other GUI components. Therefore it could be preferable to use a QT GUI application, but displaying a video in a QT GUI is not as intuitive as it is with Highgui. This tutorial will show you how to display video in a QT GUI, without the GUI becoming unresponsive. We would be creating a simple video player as shown below and would be programming in C++.
STEP 1: Create a new QT GUI Project
If you don't know how to do this check
out the guide here. The guide shows you how to create an
OpenCV console project in Qt-creator, but this time instead of using a Qt
Console Application, create a Qt GUI Application. Once created the following files are automatically added into the project:
main.cpp
|
This contains the main function
which is the starting point of all C++ applications. It is the
main function the loads the main window for the GUI application.
|
mainwindow.cpp
|
This is the source file that
contains the MainWindow class implementation.
|
mainwindow.h
|
This contains the class declaration for the MainWindow class. |
mainwindow.ui
|
This is the UI designer file that could be used to tweak the GUI.
|
<projectName>.pro | This contains settings that are used for the project compilation. |
Add widgets to GUI
Open the mainwindow.ui file, this file
could be edited manually but for this tutorial we would use the
designer.
- From the list of widgets on the left of the designer, drag in a label and two pushbutton widgets.
- Change the text on the first button to “Load Video” and on the second button to “Play”. Then Clear the text on the label. To change the text on a widget just double click on the widget the text would be highlighted, you can now change it and press enter when finished.
- Change the background colour of the label to a darker colour. The best way to change the background colour of a QT widget is to use Cascading StyleSheets or CSS. Select the label find the stylesheet property in the property window click on the button with Three dots (ellipsis) and add this line of CSS into the “Edit Style sheet” window and save it.
Background-color: #000;
- The GUI should now look something like this:
Player Class Definition
Now we add a new class to handle our video player control, we will call this the “Player” Class the following class definition should be added in player.h
#ifndef PLAYER_H #define PLAYER_H #include <QMutex> #include <QThread> #include <QImage> #include <QWaitCondition> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; class Player : public QThread { Q_OBJECT private: bool stop; QMutex mutex; QWaitCondition condition; Mat frame; int frameRate; VideoCapture capture; Mat RGBframe; QImage img; signals: //Signal to output frame to be displayed void processedImage(const QImage &image); protected: void run(); void msleep(int ms); public: //Constructor Player(QObject *parent = 0); //Destructor ~Player(); //Load a video from memory bool loadVideo(string filename); //Play the video void Play(); //Stop the video void Stop(); //check if the player has been stopped bool isStopped() const; }; #endif // VIDEOPLAYER_H
The class definition is simple and straightforward. The first thing to note is that Player class inherits from the QThread Class which will allow it to run on its own thread this is very important so that the main window remains responsive while the video is playing without this the video will cause the screen to freeze until it has finished playing. The processedImage(...) signal will be used to output the video frames to the main window (we would see how this work later).
Player Class Implementation
Here is the constructor for the Player class
Player::Player(QObject *parent) : QThread(parent) { stop = true; }
Here we simply initialise the value of the class variable stop
bool Player::loadVideo(string filename) { capture.open(filename); if (capture.isOpened()) { frameRate = (int) capture.get(CV_CAP_PROP_FPS); return true; } else return false; }
In the loadVideo() method, we use the instance of the VideoCapture class to load the video and set the frame rate. As you should already know the VideoCapture class is from the OpenCV library
void Player::Play() { if (!isRunning()) { if (isStopped()){ stop = false; } start(LowPriority); } }
The public method play() simply starts the thread by calling the run() method which is an override of the QThread run method.
void Player::run() { int delay = (1000/frameRate); while(!stop){ if (!capture.read(frame)) { stop = true; } if (frame.channels()== 3){ cv::cvtColor(frame, RGBframe, CV_BGR2RGB); img = QImage((const unsigned char*)(RGBframe.data), RGBframe.cols,RGBframe.rows,QImage::Format_RGB888); } else { img = QImage((const unsigned char*)(frame.data), frame.cols,frame.rows,QImage::Format_Indexed8); } emit processedImage(img); this->msleep(delay); } }
In the run method, we utilise a while loop to play the video after reading the frame, it is converted into a QImage and the QImage is emitted to the MainWindow object using the processedImage(...) signal; at the end of the loop we wait for a number of milliseconds (delay) which is calculated using the frame rate of the video. If the video frame was been processed it would be advisable to factor the processing time into the delay.
Player::~Player() { mutex.lock(); stop = true; capture.release(); condition.wakeOne(); mutex.unlock(); wait(); } void Player::Stop() { stop = true; } void Player::msleep(int ms){ struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; nanosleep(&ts, NULL); } bool Player::isStopped() const{ return this->stop; }
Here is the rest of the Player class, in the destructor we release the VideoCapture object and wait for the run method to exit.
MainWindow Class Definition
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QFileDialog> #include <QMessageBox> #include <player.h> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: //Display video frame in player UI void updatePlayerUI(QImage img); //Slot for the load video push button. void on_pushButton_clicked(); // Slot for the play push button. void on_pushButton_2_clicked(); private: Ui::MainWindow *ui; Player* myPlayer; }; #endif // MAINWINDOW_H
Here is the class definition for the MainWindow class, we include the clicked event slots for both buttons and a updatePlayerUI slot. We also include a myPlayer variable which is an instance of the Player Class
Mainwindow class implementation
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { myPlayer = new Player(); QObject::connect(myPlayer, SIGNAL(processedImage(QImage)), this, SLOT(updatePlayerUI(QImage))); ui->setupUi(this); }
we initialise myPlayer and we connect the signal emitted from the player class to the updatePlayerUI(...) slot, so every time a frame has been emitted it would be passed to this slot.
void MainWindow::updatePlayerUI(QImage img) { if (!img.isNull()) { ui->label->setAlignment(Qt::AlignCenter); ui->label->setPixmap(QPixmap::fromImage(img).scaled(ui->label->size() Qt::KeepAspectRatio, Qt::FastTransformation)); } }
The updatePlayerUI slot receives a QImage and resizes it to fit the label (keeping the aspect ratio) which will be used to display. It displays the image by setting the label pixmap
MainWindow::~MainWindow() { delete myPlayer; delete ui; } void MainWindow::on_pushButton_clicked() { QString filename = QFileDialog::getOpenFileName(this, tr("Open Video"), ".", tr("Video Files (*.avi *.mpg *.mp4)")); if (!filename.isEmpty()){ if (!myPlayer->loadVideo(filename.toAscii().data())) { QMessageBox msgBox; msgBox.setText("The selected video could not be opened!"); msgBox.exec(); } } } void MainWindow::on_pushButton_2_clicked() { if (myPlayer->isStopped()) { myPlayer->Play(); ui->pushButton_2->setText(tr("Stop")); }else { myPlayer->Stop(); ui->pushButton_2->setText(tr("Play")); } }
This is the remaining part of the MainWindow Class, we have the destructor for the class, "load Video"(pushbutton) button slot and the "Play" (pushbutton_2) button slot which all pretty straightforward.
Main() Function
int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow* w = new MainWindow(); w->setAttribute(Qt::WA_DeleteOnClose, true); w->show(); return a.exec(); }
And finally, the main function we create an instance of the MainWindow class and set the delete on close attribute so that the objects created are destroyed.
Final words...
This is just a simple tutorial to help anyone get started with videos in OpenCV and QT. It should also be noted that there are other ways to handle videos in QT like the Phonon multimedia framework. Please let me know if this was helpful and ask questions (if any) in the comments. Happy Coding!
UPDATE: SEE PART 2 OF THIS TUTORIAL HERE. WE SHOW YOU HOW TO ADD A TRACK-BAR TO ALLOW THE USER CONTROL THE VIDEO.
UPDATE: SEE PART 2 OF THIS TUTORIAL HERE. WE SHOW YOU HOW TO ADD A TRACK-BAR TO ALLOW THE USER CONTROL THE VIDEO.
Wednesday, 21 November 2012
Getting Started with QT and OpenCV 2.4.2
I recently started
developing with OpenCV 2.4.2 and Qt on a Linux machine (Ubuntu
12.0.4), so I decided to write a few tutorials. Here, we are going to
see how to set-up a development environment particularly Qt-creator to
work with OpenCV.
Please let me know if this was helpful and if there is tutorial you would like me to write mention it in the comments. Happy Coding!
First, you need to install
OpenCV as this has been dealt with severally I don't bother with
this. If you have not yet installed this have a look at this
tutorial
or just search Google for a tutorial that suits you.
Next step you would need to install Qt-creator. This can be done either via the Ubuntu Software Centre or by directly downloading the .bin installer from
here.
Now we can begin, launch QT creator and then
Go to File > New File or Project. Select a QT Console Application (Use a QT GUI Application if you OpenCV installation is configured to use QT and not GTK ) give the project a name e.g. “FirstQtProject”
You could leave the other
settings in the wizard as they are or change them to suit your own
projects. With the default settings, two files would be created: 'FirstQtProject.pro' ( this will vary depending on the Project Name you
have chosen) and 'main.cpp'. To run OpenCV programs in QT creator we
need to let the IDE know where to find the OpenCV libraries.
Open the .pro file
associated with the project and append the following lines at the end
of the file. This should be included in every OpenCV Project you
create but remember to change the include Path
"/usr/local/include/opencv2"
to the location of your OpenCV include directory on your PC.
#Change this to your include directory. INCLUDEPATH += "/usr/local/include/opencv2" LIBS += `pkg-config --cflags --libs opencv`If you don't have Pkg-config installed replace the last line with the following and replace “/usr/local/lib” with the location of the openCV libraries on your PC.
# Confirm the location of you opencv libraries and change appropriately. LIBS += usr/local/lib \ -lopencv_core \ -lopencv_highgui \ -lopencv_imgproc \ -lopencv_flann \ -lopencv_legacy \ -lopencv_ml \ -lopencv_features2d \ -lopencv_calib3dNow go to 'main.cpp' file and type in the following lines of code and press Ctrl+R to run it.
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> int main(void) { // Load an image from the disk and store in variable. cv::Mat image1 = cv::imread("/home/stephen/Pictures/download.jpg"); // Create a image display window called Figure1. cv::namedWindow("Figure1"); // Display image in Figure1. cv::imshow("Figure1", image1); // Wait until user closes the window or presses Esc Key. cv::waitKey(0); return 0; }The result should be an empty console window called "qtcreator_process_sub” and a window called “Figure1” containing the loaded image.

Subscribe to:
Posts (Atom)