Site : tvaira.free.fr

Mise en oeuvre d’OpenCV

OpenCV

Présentation

OpenCV (Open Computer Vision) est une bibliothèque graphique libre, initialement développée par Intel, spécialisée dans le traitement d’images en temps réel. Cette bibliothèque est distribuée sous licence BSD.

La bibliothèque OpenCV met à disposition de nombreuses fonctionnalités très diversifiées permettant de créer des programmes partant des données brutes pour aller jusqu’à la création d’interfaces graphiques basiques. Elle propose la plupart des opérations classiques en traitement bas niveau des images et des vidéos. Cette bibliothèque s’est imposée comme un standard dans le domaine de la recherche parce qu’elle propose un nombre important d’outils issus de l’état de l’art en vision des ordinateurs.

La documentation d’OpenCV (cf. OpenCV API Reference).

Retour au sommaire

Installation (Ubuntu 12.04)

Pour installer les bibliothèques de développement d’OpenCV, il faudra faire :

$ sudo apt-get install libcv-dev libhighgui-dev libopencv-dev

Les options de compilation seront :

$ pkg-config --cflags --libs opencv
-I/usr/include/opencv  -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml -lopencv_video 
-lopencv_features2d -lopencv_calib3d -lopencv_objdetect -lopencv_contrib -lopencv_legacy -lopencv_flann

Retour au sommaire

Exemple en C

OpenCV fournit une API C pour l’acquisition vidéo en provenance d’une caméra ou d’un fichier. En C, on utilisera donc les appels cvCaptureFromCAM() ou cvCaptureFromFile() qui retournent un pointeur sur une structure CvCapture. En fin de programme, il ne faudra pas oublier de le libérer en appelant cvReleaseCapture().

Dans notre cas, la caméra n’étant raccordée physiquement à l’ordinateur, on ne pourra pas utiliser l’appel cvCaptureFromCAM(). En effet, on accéde au flux vidéo via un script CGI par le réseau. On doit donc utiliser l’appel cvCaptureFromFile(). OpenCV détecte le type des données image par l’extension du fichier. La documentation de la caméra nous informe que le format des données vidéos est du type MJPEG. Au final, il faudra donc passer en paramètre de l’appel cvCaptureFromFile() : l’adresse (adresse-ip-camera:99) qui pointe vers le script CGI (c’est-à-dire videostream.cgi) fournissant les données vidéos ET l’extension .mjpg, soit :

"http://adresse-ip-camera:99/videostream.cgi?user=admin&pwd=&resolution=32&rate=0&.mjpg"

Pour réaliser une ‘capture’, l’API C met à notre disposition 3 fonctions :

  • int cvGrabFrame(CvCapture* capture) qui réalise l’acquisition de la prochaine image (frame) du fichier vidéo ou de la caméra et renvoie vrai (non nul) en cas de succès.
  • IplImage* cvRetrieveFrame(CvCapture* capture, int streamIdx=0) qui décode et renvoie l’image (frame) précedemment acquise (grab). Si il n’y a aucune image (caméra déconnectée, ou plus d’images dans le fichier vidéo), la fonction retourne un pointeur NULL.
  • IplImage* cvQueryFrame(CvCapture* capture) qui regroupe les 2 fonctions précédentes (cvGrabFrame() et cvRetrieveFrame()) en un seul appel ce qui la rend plus pratique.

Pour l’affichage, on utilisera l’appel cvNamedWindow() pour créer une fenêtre et cvShowImage() pour visualiser l’image (frame). Opencv fournit aussi des fonctions pour sauvegarder les images comme cvSaveImage().

cf. la documentation de l’API sur docs.opencv.org.

#include <stdio.h>
#include <cv.h> // contient les déclarations des structures et fonctions de manipulation d'images
#include <highgui.h> // contient déclarations des fonctions d'affichage des images

#define DEBUG

// gcc -O2 -Wall -o opencv-0 opencv-0.c `pkg-config --cflags --libs opencv`

int main(int argc, char *argv[])
{
    // cf. http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html
    CvCapture *capture = cvCaptureFromFile(
    "http://192.168.52.14:99/videostream.cgi?user=admin&pwd=&resolution=32&rate=0&.mjpg");
    if(!capture)
    {
        printf("Erreur d'initialisation !\n");
        exit(1);
    }

    //cvNamedWindow("Wanscam");
    cvNamedWindow("Wanscam", CV_WINDOW_AUTOSIZE);
    
    while(1)
    {    
        #ifdef DEBUG
        double t1 = (double)cvGetTickCount();
        #endif
        
        // Méthode 1 :
        /*if(!cvGrabFrame(capture))
        {
            printf("Erreur d'acquisition !\n");
            exit(1);
        }
        IplImage *img = cvRetrieveFrame(capture);*/

        // Méthode 2 :
        IplImage *img = cvQueryFrame(capture);
        if(img == NULL)
        {
            printf("Erreur de lecture !\n");
            exit(1);
        }
        
        #ifdef DEBUG
        printf("%dx%d pixels (%d canaux couleurs)\n", img->width, img->height, img->nChannels); 
        double t2 = (double)cvGetTickCount();
        printf("time: %gms  fps: %.2g\n", 
               (t2-t1)/(cvGetTickFrequency()*1000.), 1000./((t2-t1)/(cvGetTickFrequency()*1000.)));
        #endif    

        cvShowImage("Wanscam", img);
    
        // Si on veut sauvegarder l'image
        //cvSaveImage("foo.jpg", img);
    
        //cvReleaseImage(&img);
    
        // Appuyez sur une touche pour sortir
        if(cvWaitKey(0) >= 0) break;
    }    
    
    cvReleaseCapture(&capture);
    
    return 0;
}

Le source de l’exemple C.

Retour au sommaire

Exemple en C++

En C++, OpenCV fournit une classe VideoCapture pour l’acquisition vidéo en provenance d’une caméra ou d’un fichier. On utilisera la méthode open() pour ouvrir le flux vidéo de la caméra.

Pour rappel, on accéde au flux vidéo via un script CGI par le réseau. OpenCV détecte le type des données image par l’extension du fichier. La documentation de la caméra nous informe que le format des données vidéos est du type MJPEG. Au final, il faudra donc passer en paramètre de l’appel open() : l’adresse (adresse-ip-camera:99) qui pointe vers le script CGI (c’est-à-dire videostream.cgi) fournissant les données vidéos ET l’extension .mjpg, soit :

"http://adresse-ip-camera:99/videostream.cgi?user=admin&pwd=&resolution=32&rate=0&.mjpg"

Pour réaliser une ‘capture’, l’API C++ met à notre disposition 3 méthodes :

  • bool VideoCapture::grab() qui réalise l’acquisition de la prochaine image (frame) du fichier vidéo ou de la caméra et renvoie vrai (true) en cas de succès.
  • bool VideoCapture::retrieve(Mat& image, int channel=0) qui décode et renvoie l’image (frame) précedemment acquise (grab). Si il n’y a aucune image (caméra déconnectée, ou plus d’images dans le fichier vidéo), la fonction retourne faux (false).
  • bool VideoCapture::read(Mat& image) qui regroupe les 2 fonctions précédentes (cvGrabFrame() et cvRetrieveFrame()) en un seul appel ce qui la rend plus pratique. L’opérateur >> peut aussi être utilisé.

Pour manipuler des images, OpenCV utilise une classe Mat (cf. cv::Mat).

Pour l’affichage des images, on utilisera la méthode cv::imshow(). Opencv fournit aussi des méthodes pour lire des images comme imread() et pour les sa uvegarder avec imwrite().

cf. la documentation de l’API sur docs.opencv.org.

#include <iostream>
#include <cv.h> // contient les déclarations des classes de manipulation d'images
#include <highgui.h> // contient déclarations des fonctions d'affichage des images

#define DEBUG

using namespace cv;
using namespace std;

// g++ -o opencv-1 opencv-1.cpp `pkg-config --cflags --libs opencv`

int main(int, char**) 
{
    VideoCapture capture;
    Mat img;

    const string adresseFluxVideo = 
    "http://192.168.52.14:99/videostream.cgi?user=admin&pwd=&resolution=32&rate=0&.mjpg"; 

    if(!capture.open(adresseFluxVideo)) 
    {
        cout << "Erreur d'initialisation !" << endl;
        return 1;
    }

    #ifdef DEBUG
    double dWidth = capture.get(CV_CAP_PROP_FRAME_WIDTH);
    double dHeight = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
    cout << dWidth << " x " << dHeight << endl;
    #endif    

    //namedWindow("Wanscam");
    namedWindow("Wanscam", CV_WINDOW_AUTOSIZE);

    while(1)
    {            
        // Méthode 1 :
        /*if(!capture.grab())
        {
            cout << "Erreur d'acquisition !" << endl;            
            exit(1);
        }
        if(!capture.retrieve(img))
        {
            cout << "Erreur de récupération !" << endl;            
            exit(1);
        }*/

        // Méthode 2 :
        if(!capture.read(img)) 
        {
            cout << "Erreur de lecture !" << endl;
            return 1;
        }

        imshow("Wanscam", img);
            
        // Si on veut sauvegarder l'image
        //imwrite("foo.jpg", img);
    
        // Appuyez sur une touche pour sortir
        if(cv::waitKey(0) >= 0) break;
    }       
    
    return 0;
}

OpenCV fournit de très nombreuses fonctionnalités. Voici un exemple de deux transformations possibles sur des objets image de type Mat :

// ...

namedWindow("Flip", CV_WINDOW_AUTOSIZE);
namedWindow("Negative", CV_WINDOW_AUTOSIZE);

while(1)
{
    // ...
    flip(img, img, 1);
    imshow("Flip", img);

    Mat gray, edge, draw;
    cvtColor(img, gray, CV_BGR2GRAY); 
    Canny(gray, edge, 50, 150, 3); 
    edge.convertTo(draw, CV_8U);
    imshow("Negative", draw);        

    // ...
}
    
// ...

Le source de l’exemple C++.

Retour au sommaire