Tag Archives: OpenCV

Screenshoot

Software

In questa pagina è raccolto tutto il codice realizzato per il controllo del robot e dei sui dispositivi, commentato nelle altre sezioni del sito.
Può capitare che nonostante tutti i controlli e i test effettuati il codice proposto contenga dei bugs (chi riesce a programmare senza?). Se ne trovaste qualcuno o semplicemente volete segnalarmi dei miglioramenti che secondo voi possono essere apportati la mia casella email è sempre a vostra disposizione.

Oltre al codice utilizzato sul Rover è disponibile una raccolta di tutorial sulla Computer Vision e la programmazione in generale.

Microchip PIC

  • Ambiente di Sviluppo SW PIC. Un piccolo tutorial sull’installazione e la configurazione dell’ambiente di programmazione per PIC in linguaggio C. E’ illustrata anche la realizzazione di un piccolo progetto e il suo debug in simulazione [Vecchia versione del sito]
  • Controllo Servo R/C . Codice C per PIC 16f628, per il controllo di un Servo Motore da modellismo [Vecchia versione del sito]

Computer Vision

[Tutorial OpenCV Qt] OpenGL Widget per visualizzare immagini da OpenCV in una GUI con Qt (Seconda Parte)

Dopo aver creato il Widget Qt per visualizzare immagini OpenCV utilizzando OpenGL nella prima parte del tutorial, non resta che vedere come utilizzarlo.

La semplice applicazione che realizzeremo permette di visualizzare il flusso video proveniente da una webcam, è la base di partenza per ogni applicazione che utilizza OpenCV.

Questo tutorial prevede la conoscenza di base di Qt Creator e di Qt. Se qualche passaggio non vi risulta chiaro o sufficientemente spiegato non esitate a contattarmi via email.

Iniziamo aprendo l’applicazione QtCreator, Qt può essere utilizzato anche in altri ambienti di lavoro (Eclipse, Visual Studio, …), ma sinceramente preferisco utilizzare il suo ambiente originale in quanto è stato appositamente realizzato per sfruttare tutte le caratteristiche di Qt.

Creiamo un nuovo progetto “Qt Widget Project” -> “Qt GUI Application”.

Copiamo nella cartella del progetto i file creati nella prima parte del tutorial.

Aggiungiamo al progetto il file “cqtopencvviewergl.h” e “cqtopencvviewergl.cpp”.

Aggiungiamo alla GUI il Widget OpenCV-OpenGL:

  • apriamo il form
  • inseriamo un widget standard prendendolo dal menu “Containers”
  • chiamiamo il widget “openCVviewer”
  • clicchiamo con il tasto destro sul widget e selezioniamo “Promote to”
  • nella finestra che si apre selezioniamo “Add” e nella casella “Promoted Class Name” inseriamo “CQtOpenCVViewerGl”
  • Clicchiamo con il destro sulla GUI, selezioniamo Layout e quindi “Layout in a Grid” per espandere il widget su tutta l’area disponibile

La base dell’interfaccia è pronta. Ora dobbiamo configurare l’applicazione in modo che possa utilizzare OpenCV:

  • Apriamo il file “pro” e aggiungiamoINCLUDEPATH += your openCV path(for Linux: INCLUDEPATH += /usr/local/opencv2/)
  • aggiungiamo ancheLIBS     += -lopencv_core -lopencv_highgui

Ora che l’applicazione è predisposta all’utilizzo di OpenCV possiamo inserire il codice per la visualizzazione delle immagini dalla Webcam.

Apriamo ancora il form e inseriamo una voce “Start” al menu:

  • selezioniamo la barra di menu dove la label “Type here” e inseriamo la voce “Camera”
  • Selezioniamo la voce camera e nel sottomenu in “Type here” digitiamo “Start

Nel menu “Action Editor” clicchiamo con il tasto destro su “actionStart” e selezioniamo “Go to slot…”->”triggered()”.

QtCreator in questo modo crea automaticamente la funzione che verrà chiamata quando l’utente selezionerà “Camera”->”Start”.

La nostra funzione sarà molto semplice:

void QtGLWebcamDemo::on_actionStart_triggered()
{
    if( !mCapture.isOpened() )
        if( !mCapture.open( 0 ) )
            return;

    startTimer(50);
}

mCapture è una variabile privata della nostra GUI:

    cv::VideoCapture mCapture;

Ricordiamoci inoltre che per utilizzare VideoCapture è necessario includere il modulo “HighGUI” di OpenCV nella nostra applicazione:

#include

L’applicazione deve gestire il timer che abbiamo appena creato in modo che ogni 50 msec chieda alla webcam una nuova immagine e la invii al nostro widget opengl.

Aggiungiamo alla nostra classe principale la gestione dell’evento Timer:

protected:
     void timerEvent(QTimerEvent *event);

Che conterrà il seguente semplicissimo codice:

void QtGLWebcamDemo::timerEvent(QTimerEvent *event)
{
    cv::Mat image;
    mCapture >> image;

    // Do what you want with the image <img class="wp-smiley" alt=":-)" src="/web/20120417211944im_/http://www.robot-home.it/blog/wp-includes/images/smilies/icon_smile.gif" /> 

    // Show the image
    ui-&gt;openCVviewer-&gt;showImage( image );
}

Siamo arrivati alla fine. La semplice applicazione per visualizzare il flusso video da una webcam utilizzando Qt, OpenCV e OpenGL è pronta, non resta che compilare e provare.

Se volete scaricare i file del progetto demo realizzaro per questo tutorial, seguite questo link.

Buon divertimento :)

work in progress

Il primo tutorial è tornato online

Sono felice di annunciare che uno dei tutorial a cui tenevo di più non è andato perso, ma è tornato subito online.

La pagina “[Tutorial OpenCV Qt] OpenGL Widget per visualizzare immagini da OpenCV in una GUI con Qt” e di nuovo disponibile nella sezione Informatica.

Continuate a seguirmi

[Tutorial OpenCV Qt] OpenGL Widget per visualizzare immagini da OpenCV in una GUI con Qt (Prima Parte)

In questo tutorial sarà realizzato un Widget Qt derivato da QGLWidget per visualizzare in un’interfaccia grafica le immagini OpenCV (cv::Mat).
Il widget permette una miglior visualizzazione dell’immagine mantenendo il rapporto “larghezza/altezza” della stessa se il Widget viene ridimensionato.

Il tutorial presuppone la conoscenza di base del linguaggio C++, del framework Qt4, dell’ambiente di sviluppo QtCreator e della libreria OpenCV2. Il codice di esempio è stato realizzato per Windows7, ma i file di progetto allegati possono essere adattati per Linux o MacOS senza grossa difficoltà, unicamente settando i corretti “path” per le librerie nel file “.pro” seguendo gli appositi commenti.

La prima parte del tutorial descriverà la realizzazione del widget.

CQtOpenCVViewerGl: realizzazione

In ambiente QtCreator:

  • File -> New File or Project -> C++ -> C++ Class -> Choose
  • Class name: CQtOpenCVViewerGl
  • Base Class: QGLWidget [attenzione a maiuscole e minuscole!]

A questo punto troverete tra i file del progetto che state realizzando due nuovi file: cqtopencvviewergl.cpp e cqtopencvviewergl.h

Aprite il file cqtopencvviewergl.h e aggiungete le seguenti variabili private:

private:
   bool mSceneChanged; /// Indicates when OpenGL view is to be redrawn

   QImage mRenderQtImg; /// Qt image to be rendered
   cv::Mat mOrigImage; /// original OpenCV image to be shown

   QColor mBgColor; /// Background color

   int mOutH; /// Resized Image height
   int mOutW; /// Resized Image width
   float mImgratio; /// height/width ratio

   int mPosX; /// Top left X position to render image in the center of widget
   int mPosY; /// Top left Y position to render image in the center of widget
  • mSceneChanged: indica se il frame openGL deve essere renderizzato
  • mRenderQtImg: contiene l’immagine da visualizzare in formato Qt
  • mOrigImage: contiene l’immagine da visualizzare in formato OpenCV
  • mBgColor: è il colore dello sfondo del widget non occupato dall’immagine
  • mOutH & mOutW: sono le dimensioni dell’immagine scalata per poter essere interamente visualizzata nel widget mantenendo il giusto rapporto tra altezza e larghezza
  • mImgratio: mantiene informazioni sul rapporto larghezza/altezza dell’immagine
  • mPosX & mPosY: sono le coordinate del punto in alto a sinistra dell’immagine in modo che sia renderizzata al centro del widget

A questo punto è necessario aggiungere la dichiarazione di alcune funzioni.
Innanzitutto aggiungiamo una funzione di signal e una di slot:

signals:
   void imageSizeChanged( int outW, int outH ); /// Used to resize the image outside the widget

public slots:
   bool showImage( cv::Mat image ); /// Used to set the image to be viewed
  • void imageSizeChanged( int outW, int outH ): serve a comunicare al “mondo Qt” che il widget ha subito un “resize” e che l’immagine renderizzata avrà dimensioni “outW e outH”. Questo è utile se si vuole fornire al widget un’immagine già riscalata in modo da non obbligare il widget ad effettuare le operazioni di “rescaling” descritte in seguito
  • bool showImage( cv::Mat image ): è la funzione utilizzata per “passare” al widget l’immagine da visualizzare. E’ uno slot in modo da poter essere utilizzata nel meccanismo signal/slot di Qt

Infine inseriamo la dichiarazione delle cinque funzioni utilizzate per il rendering dell’immagine:

protected:
   void initializeGL(); /// OpenGL initialization
   void paintGL(); /// OpenGL Rendering
   void resizeGL(int width, int height); /// Widget Resize Event

   void updateScene(); /// Forces a scene update
   void renderImage(); /// Render image on openGL frame
  • void initializeGL(): reimplementa la funzione di inizializzazione dell’ambiente openGL
  • void paintGL(): reimplementa la funzione di rendering openGL
  • void resizeGL(int width, int height): reimplementa la funzione di “resize” del “layout” openGL
  • void updateScene(): forza il rendering del widget
  • void renderImage(): è chiamata da paintGL per renderizzare l’immagine

Finite le dichiarazioni si può passare all’inizializzazione delle variabili e alla definizione delle funzioni nel file cqtopencvviewergl.cpp.
Inizializzazione delle variabili nel costruttore:

CQtOpenCVViewerGl::CQtOpenCVViewerGl(QWidget *parent) :
QGLWidget(parent)
{
   mSceneChanged = false;
   mBgColor = QColor::fromRgb(150, 150, 150);

   mOutH = 0;
   mOutW = 0;
   mImgratio = 4.0f/3.0f; // Default image ratio

   mPosX = 0;
   mPosY = 0;
}

Definizione delle funzioni:

void CQtOpenCVViewerGl::initializeGL()
{
   makeCurrent();
   qglClearColor(mBgColor.darker());
}

Imposta il colore di background della scena OpenGL.
Questa funzione, come tutte le successive inizia con una chiamata a makeCurrent utile nel caso la GUI che conterrà il widget contenga più di un widget OpenGL.

void CQtOpenCVViewerGl::resizeGL(int width, int height)
{
   makeCurrent();
   glViewport(0, 0, (GLint)width, (GLint)height);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   glOrtho(0, width, 0, height, 0, 1); // To Draw image in the center of the area

   glMatrixMode(GL_MODELVIEW);

   // ---> Scaled Image Sizes
   mOutH = width/mImgratio;
   mOutW = width;

   if(mOutH>height)
   {
      mOutW = height*mImgratio;
      mOutH = height;
   }

   emit imageSizeChanged( mOutW, mOutH );
   // < --- Scaled Image Sizes

   mPosX = (width-mOutW)/2;
   mPosY = (height-mOutH)/2;

   mSceneChanged = true;

   updateScene();
}

Questa funzione è chiamata ogni volta il widget viene ridimensionato.

  • Righe 3-12: inizializzazione del layout openGL per il rendering dell’immagine in 2D
  • Righe 14-21: calcolo delle dimensioni dell’immagine renderizzata in modo che sia rispettato il rapporto larghezza/altezza e che l’immagine venga visualizzata tutta
  • Riga 23: emissione del segnale “imageSizeChanged” per comunicare l’informazione al “mondo Qt” come descritto in precedenza
  • Righe 26-27: calcolo della posizione del vertice in alto a sinistra dell’immagine in modo che sia centrata nel widget
  • Righe 29-31: comunicazione al widget che c’è una nuova immagine da visualizzare
void CQtOpenCVViewerGl::updateScene()
{
   if( mSceneChanged && this->isVisible() )
   updateGL();
}

updateScene serve a “forzare” il rendering della scena dopo che l’immagine è stata aggiornata. Per forzare il rendering è necessaria una chiamata a updateGL che viene però effettuata solo se la scena è effettivamente cambiata e se il widget è visibile, in modo da evitare l’inutile esecuzione di codice “pesante”.

void CQtOpenCVViewerGl::paintGL()
{
   makeCurrent();

   if( !mSceneChanged )
      return;

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   renderImage();

   mSceneChanged = false;
}

paintGL è la funzione chiamata da Qt quando si rende necessario un aggiornamento dell’area di un widget OpenGL.
Nel nostro caso la funzione si occupa solo di “pulire” la scena e chiamare renderImage che si occuperà dell’effetivo rendering della nostra immagine.

void CQtOpenCVViewerGl::renderImage()
{
    makeCurrent();

    glClear(GL_COLOR_BUFFER_BIT);

    if (!mRenderQtImg.isNull())
    {
        glLoadIdentity();

        QImage image; // the image rendered

        glPushMatrix();
        {
            int imW = mRenderQtImg.width();
            int imH = mRenderQtImg.height();

            // The image is to be resized to fit the widget?
            if( imW != this->size().width() &&
                    imH != this->size().height() )
            {

                image = mRenderQtImg.scaled( //this->size(),
                                             QSize(mOutW,mOutH),
                                             Qt::IgnoreAspectRatio,
                                             Qt::SmoothTransformation
                                             );

                //qDebug( QString( "Image size: (%1x%2)").arg(imW).arg(imH).toAscii() );
            }
            else
                image = mRenderQtImg;

            // --->Centering image in draw area

            glRasterPos2i( mPosX, mPosY );
            // < --- Centering image in draw area

            imW = image.width();
            imH = image.height();

            glDrawPixels( imW, imH, GL_RGBA, GL_UNSIGNED_BYTE, image.bits());
        }
        glPopMatrix();

        // end
        glFlush();
    }
}

renderImage è la funzione principale del widget, infatti si occupa del disegno dell’immagine memorizzata.

  • Righe 19-32: questa parte di codice si occupa del ridimensionamento dell’immagine in modo che venga visualizzata completamente nel widget. Da notare che se le dimensioni sono giuste l’operazione è saltata, con un notevole risparmio di potenza di calcolo. Per questo esiste il signal imageSizeChanged per comunicare al resto dell’applicazione quali sono le dimensioni effettive dell’immagine renderizzata.
  • Riga 36: indica la posizione del pixel in alto a sinistra dell’immagine in modo che la stessa sia sempre posizionata al centro del widget
  • Riga 42: chiamata alla funzione openGL glDrawPixels per il rendering effettivo dell’immagine
bool CQtOpenCVViewerGl::showImage( cv::Mat image )
{
    image.copyTo(mOrigImage);

    mImgratio = (float)image.cols/(float)image.rows;

    if( mOrigImage.channels() == 3)
        mRenderQtImg = QImage((const unsigned char*)(mOrigImage.data),
                              mOrigImage.cols, mOrigImage.rows,
                              mOrigImage.step, QImage::Format_RGB888).rgbSwapped();
    else if( mOrigImage.channels() == 1)
        mRenderQtImg = QImage((const unsigned char*)(mOrigImage.data),
                              mOrigImage.cols, mOrigImage.rows,
                              mOrigImage.step, QImage::Format_Indexed8);
    else
        return false;

    mRenderQtImg = QGLWidget::convertToGLFormat(mRenderQtImg);

    mSceneChanged = true;

    updateScene();

    return true;
}

Ultima, ma non meno importante, è la funzione necessaria a comunicare al widget quale immagine visualizzare.
showImage accetta in ingresso immagini OpenCV (cv::Mat) a 8 bit con 1 o 3 canali e provvede a convertirle nel formato accettato da QGLWidget.
La funzione calcola e memorizza anche il rapporto larghezza/altezza (riga 5) utilizzato durante il ridimensionamento della stessa.

Il codice completo del widget è scaricabile tramite questo link.

Un esempio di utilizzo è disponibile nella seconda parte del tutorial.

%d bloggers like this: