[Tutorial] Nokia Qt4 & OpenCV 2.1 C++
Visualizzare un flusso video o un’immagine su un’interfaccia grafica di propria realizzazione non è una delle operazioni più banali da realizzare se non si conoscono le basi di programmazione di GUI. Questo tutorial spiegherà nel modo più semplice possibile le operazioni da seguire per visualizzare immagini da OpenCV 2.1 in una GUI realizzata con Qt4 utilizzando il framework C++ introdotto in OpenCV dalla versione 2.0.
Il tutorial è stato realizzato utilizzando l’ambiente di sviluppo “Eclipse Helios” con installato il PlugIn “Qt Eclipse Integrator”. Il compilatore utilizzato è MinGW nella versione fornita con l’installazione “completa” di Qt SDK.
Molti concetti di programmazione Qt e di installazione dell’ambiente di sviluppo Eclipse e OpenCV sono dati per scontati, per approfondire consiglio la lettura del libro C++ GUI Programming with Qt4 (2nd Edition), la cui prima edizione è disponibile online gratuitamente.
Per chi è abituato ad utilizzare OpenCV con il linguaggio C deve innanzitutto abbandonare l’idea della differenza che esisteva tra CvMat e IplImage. OpenCV in modalità C++ utilizza un oggetto unico per la rappresentazione di immagini e dati matriciali: cv::Mat.
Ma partiamo dall’inizio… o meglio dalla fine: l’interfaccia dell’applicazione, la GUI
La GUI sarà composta da una zona di rendering delle immagini e da alcuni pulsanti di “azione” posti sulla sinistra. Per il rendering delle immagini verrà adottata la “tecnica della QLabel”: non verrà realizzato un Widget apposito, ma si utilizzeranno le caratteristiche del Widget Qt QLabel.
Al flusso video proveniente dalla Webcam potrà essere applicato un filtro di Canny:
o un filtro di estrazione dei segmenti tramite algoritmo di Hough probabilistico:
il progetto completo può essere scaricato qui.
C++ GUI Programming with Qt 4 (2nd Edition)
Questo è un video dell’applicazione di esempio:
Per poter renderizzare un’immagine in una finestra di dialogo Qt è sufficiente creare un oggetto QLabel e utilizzare il seguente codice:
void TestOpenCV_GUI::on_loadImageButton_clicked()
{
mImage.release(); // Reinizializzazione dell'immagine
Mat tmpImg = imread( "puzzle.bmp" );
cvtColor( tmpImg, mImage, CV_BGR2RGB);
QImage tmp( (uchar*)mImage.data, mImage.cols, mImage.rows, mImage.step,
QImage::Format_RGB888 );
ui.imageFrame->setPixmap( QPixmap::fromImage( tmp ) );
ui.fpsLabel->setText( "puzzle.bmp loaded");
}
Il rendering dello stream dalla webcam si ottiene grazie a un oggetto “QTimer“:
QTimer mCameraTimer;
al suo setting nella funzione di gestione del click sul pulsante “Start/Stop Camera”:
void TestOpenCV_GUI::on_cameraButton_clicked()
{
if( !mCameraRunning )
{
mCap.open( CV_CAP_ANY );
if( !mCap.isOpened() )
return;
bool res = mCap.set( CV_CAP_PROP_FRAME_WIDTH, 640.0 );
res = mCap.set( CV_CAP_PROP_FRAME_HEIGHT, 480.0 );
res = mCap.set( CV_CAP_PROP_FPS, 100.0 );
mCameraRunning = true;
mCameraTimer.start( 20 ); // 50 fps (richiesti)
connect( &mCameraTimer, SIGNAL( timeout() ),
this, SLOT( on_cameraTimerTimeout() ) );
ui.cameraButton->setText( "Stop Camera" );
ui.loadImageButton->setEnabled( false );
ui.cannyButton->setEnabled( false );
}
else
{
if( !mCap.isOpened() )
return;
mCameraTimer.stop();
mCap.release();
mCameraRunning = false;
ui.cameraButton->setText( "Start Camera" );
ui.loadImageButton->setEnabled( true );
ui.cannyButton->setEnabled( true );
}
}
e alla funzione di gestione dell’evento “Timeout” del timer:
void TestOpenCV_GUI::on_cameraTimerTimeout()
{
if( mCameraRunning && mCap.isOpened() )
{
mCap >> mImage; // Nuovo frame dalla camera
// ---> Calcolo e visualizzazione FPS
int time = mTime.elapsed();
mTime.restart();
mFPS = (1.0/(double)time)*1000.0;
QString str = QString( "FPS: %1" ).arg( mFPS );
ui.fpsLabel->setText( str );
// < --- Calcolo e visualizzazione FPS
cvtColor( mImage, mImage, CV_BGR2RGB);
if( ui.houghCheckBox->checkState() == Qt::Checked )
{
Mat gray, color;
cv::cvtColor( mImage, gray, CV_RGB2GRAY );
GaussianBlur( gray, gray, Size(5, 5), 2, 2 );
cv::Canny( gray, gray, 20, 60, 3 );
vector lines;
HoughLinesP( gray, lines, 1, CV_PI/180, 80, 30, 10 );
mImage.copyTo( color );
for( size_t i = 0; i < lines.size(); i++ )
{
line( color, Point(lines[i][0], lines[i][1]),
Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 2, 8 );
}
QImage tmp( (uchar*)color.data, color.cols, color.rows,
color.step, QImage::Format_RGB888 );
ui.imageFrame->setPixmap( QPixmap::fromImage( tmp ) );
}
else if( ui.cannyCheckBox->checkState() == Qt::Checked )
{
Mat gray;
cv::cvtColor( mImage, gray, CV_RGB2GRAY );
GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
cv::Canny( gray, gray, 20, 60, 3 );
QImage tmp( (uchar*)gray.data, gray.cols, gray.rows,
gray.step, QImage::Format_Indexed8 );
ui.imageFrame->setPixmap( QPixmap::fromImage( tmp ) );
}
else
{
QImage tmp( (uchar*)mImage.data, mImage.cols, mImage.rows,
mImage.step,
QImage::Format_RGB888 );
ui.imageFrame->setPixmap( QPixmap::fromImage( tmp ) );
}
}
}
Vorrei farvi notare la semplicità del rendering di immagini RGB o in bianco/nero in Qt.
Se osservete le righe di codice 36 e 44 noterete che per le immagini a colori ho utilizzato
QImage::Format_RGB888
mentre per quelle in bianco/nero
QImage::Format_Indexed8
Tutto il rimanente codice è identico!
Per quanto riguarda la realizzazione della GUI vi rimando al libro che vi ho consigliato sopra… una trentina di pagine di lettura e partirete come razzi.
Il tutorial è terminato, spero vi sia stato utile nel qual caso ricordatevi di offrirmi un caffè.
Per ogni dubbio, domanda, critica non esitate a inviare commenti utilizzando l’apposito thread del forum.









Great and simple code! Thank you very much!
First time that I found one which ran almost out of the box (Mac OS X with Qt 4.7 and OpenCV 2.2 installed via MacPorts).
To compile it under Linux/Mac OS X just add:
unix|macx {
INCLUDEPATH += /opt/local/include /usr/local/include
LIBS += -L/opt/local/lib -lopencv_core -lopencv_imgproc -lopencv_highgui
TARGET += release
}
One Problem: When I use the CANNY, the picture is distorted, red, green, and half the size and two times in the frame instead of black/white.
My Webcam is 640x480.
Any help / idea would be great!!!
[...] installiert unter Mac OS X via MacPorts. Der Sourcecode wurde auf dieser (italienischen) Seite gefunden. Es muss nur noch herausgefunden werden, warum der Canny-Algorithmus nicht so läuft, wie er [...]
Canny filter works on greyscale images and result itself is in greyscale format.
On Windows Format_Indexed8 is the right way to render greyscale images, I don’t the correct type for Mac, sorry.
To the publisher of the document: WOW!! Can’t thank you enough. This was a great tutorial for getting a complete opencv Newbie started. I was able to adapt your code to be able to output a greyscale image and a colour image at the same time. I am trying to implement motion and event detection. What libraries should I be looking at to achieve this? Thanks in advance for any hints.
@Markus Thanks for the tip, it tidied up my include path a lot.
Hi Inprimus, thank you for your beautiful words.
To implement motion and event detection take a look at these pages of OpenCV manual:
- http://opencv.willowgarage.com/documentation/cpp/video_motion_analysis_and_object_tracking.html
- http://opencv.willowgarage.com/documentation/cpp/ml_statistical_models.html?highlight=statmodel#CvStatModel
@Myzhar If you’re ever in England, drop me a mail. I owe you several beers and a meal.