Retour


Pour toute remarque concernant cette page : maintainer CHEZ deb-indus POINT org

Sommaire

Introduction
La gestion de la caméra
Les fonctions et objets opencv
Le fonctionnement de visicolor
Visicolor V2.0 et V0.3


Introduction

Visicolor est un petit logiciel sous licence GPL permettant de récupérer un flux video venant d’une webcam, de l’intégrer dans un environnement opencv et d’effectuer une analyse de colorimétrie très succinte.
Etant donné qu’il vous faudra certainement mettre les mains dedans pour l’adapter à votre matériel je vous conseille de lire cette page dans un premier temps.

Si vous êtes super pointu sur opencv vous allez vous embêter, sinon, la suite peut faire office de tutoriel sur l’emploi de la librairie.

Modifications du 24 avril 2008 :

Quelques changements au programme.
La version 0.1 de visicolor a disparu. Il reste ces deux versions (paquet debian et tar.gz) :
Les téléchargements ont déménagés sur une nouvelle page

sommaire

La gestion de la caméra

Le driver

Quand on branche une webcam USB sous linux celle-ci apparaît en /dev/video0, si on en branche une deuxième elle apparaîtra en /dev/video1 et ainsi de suite. Le périphérique qui sera attaqué par visicolor sera donc /dev/video0 .
Ceci est valable quand la cam est bien prise en charge, pour mes essais j’ai utilisé une Logitech QuickCam, celle-ci est bien prise en charge par le driver gspca.
Pour l’installer, faites un petit apt-cache search gspca, sur les dépôts officiels voici le résultat :

gspca-modules-2.6-486 - gspca video for linux (v4l) driver modules for Linux 2.6 on x86
gspca-modules-2.6-686 - gspca video for linux (v4l) driver modules for Linux 2.6 on PPro/Celeron/PII/PIII/P4
gspca-modules-2.6-686-bigmem - gspca video for linux (v4l) driver modules for Linux 2.6 on PPro/Celeron/PII/PIII/P4
gspca-modules-2.6-amd64 - gspca video for linux (v4l) driver modules for Linux 2.6 on AMD64
gspca-modules-2.6-k7 - gspca video for linux (v4l) driver modules for Linux 2.6 on AMD K7
gspca-modules-2.6-vserver-686 - gspca video for linux (v4l) driver modules for Linux 2.6 on PPro/Celeron/PII/PIII/P4
gspca-modules-2.6-vserver-k7 - gspca video for linux (v4l) driver modules for Linux 2.6 on AMD K7
gspca-modules-2.6-xen-686 - gspca video for linux (v4l) driver modules for Linux 2.6 on i686
gspca-modules-2.6-xen-vserver-686 - gspca video for linux (v4l) driver modules for Linux 2.6 on i686
gspca-modules-2.6.18-6-486 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on x86
gspca-modules-2.6.18-6-686 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on PPro/Celeron/PII/PIII/P4
gspca-modules-2.6.18-6-686-bigmem - gspca video for linux (v4l) driver modules for Linux 2.6.18 on PPro/Celeron/PII/PIII/P4
gspca-modules-2.6.18-6-amd64 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on AMD64
gspca-modules-2.6.18-6-k7 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on AMD K7
gspca-modules-2.6.18-6-vserver-686 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on PPro/Celeron/PII/PIII/P4
gspca-modules-2.6.18-6-vserver-k7 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on AMD K7
gspca-modules-2.6.18-6-xen-686 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on i686
gspca-modules-2.6.18-6-xen-vserver-686 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on i686
gspca-source - source for the gspca v4l kernel module
gspca-modules-2.6.18-5-686 - gspca video for linux (v4l) driver modules for Linux 2.6.18 on PPro/Celeron/PII/PIII/P4

Choisissez celui qui convient à votre noyau et installez-le, ou alors compilez-le.

Si vous avez des problèmes de prise en charge de votre matériel je vous conseille cette page, vous y trouverez sûrement plein d’informations pratiques.
Ici je considérerai que la webcam est une Quick Cam.

Ouverture - fermeture du périphérique

Maintenant que nous avons une caméra fonctionnelle voyons comment la piloter depuis le code source.
Quand j’ai commencé à utiliser opencv je gérais la caméra avec mais j’ai eu beaucoup de soucis, par exemple je lançais mon appli, j’obtenais une belle image, je quittais l’appli et je la relançais, l’image étais mauvaise (paramètres de luminosité et contraste partis dans le puits). Du coup je n’utilise plus opencv pour manager la cam, je me suis fait les outils qui vont bien.

Le premier : comment prendre la main dessus et la rendre.

// Ouverture de la camera
int openCam( void )
{
	descrpFichierCam1 = open( CAM1_DEVICE, O_RDWR );
	if(descrpFichierCam1 < 0)
	{
		printf("Aucun peripherique detecte\n");
		printf("Verifiez les droits d'acces a /dev/video0\n");
		return 1;
	}
	return 0;
}

// Fermeture de la camera
void closeCam( void )
{
	if ( descrpFichierCam1 > 0 )
		close(descrpFichierCam1);
}

CAM1_DEVICE est une macro pour /dev/video0
On la considère comme toujours sous linux : comme un fichier que l’on ouvre et ferme. A ceci près qu’on l’ouvre en lecture/écriture.
Pensez bien à vérifier les droits de votre utilisateur sur /dev/video0 et de même pensez à l’ajouter au groupe camera.

Configuration du périphérique

Maintenant voyons comment lui demander ses capacités et régler ses paramètres.
Personnellement j’utilise le driver userspace ioctl, cela me permet d’envoyer des ordres et de récupérer des données facilement.

// Informations sur les capacites du peripherique
void printCap( void )
{
	// acces aux proprietes de la cam
	if(ioctl(descrpFichierCam1, VIDIOCGCAP, &vidcap)< < 0) exit(-1);
	// affichage dans la sortie
	printf("-------------------------------------------------------\n");
	printf("Nom: %s\n", vidcap.name);
	if(!(vidcap.type && VID_TYPE_CAPTURE))
	{
		printf("Le peripherique detecte ne supporte pas la capture\n");
		exit(-1);
	}
	printf("Type: Camera\n");
	printf("Audio: %d\n", vidcap.audios);
	printf("Largeur max.: %d\n", vidcap.maxwidth);
	printf("Longueur max.: %d\n", vidcap.maxheight);
	printf("Largeur min.: %d\n", vidcap.minwidth);
	printf("Longueur min.: %d\n", vidcap.minheight);
	printf("-------------------------------------------------------\n");
}

// Definition des proprietes de l'image
void getImage( void )
{
	// acces aux parametres d'image de la cam
	if(ioctl(descrpFichierCam1, VIDIOCGPICT, &image)<0)
	{
		perror("VIDIOCGPICT");
		exit(-1);
	}
	printf("Donnees lues\n");
	printf("Hue: %d\n", image.hue);
	printf("Color: %d\n",image.colour);
	printf("Contrast: %d\n", image.contrast);
	printf("Brightness: %d\n", image.brightness);
	printf("Whiteness: %d\n", image.whiteness);
	printf("Depth: %d\n", image.depth);
	printf("-------------------------------------------------------\n");
}

// Definition des proprietes de l'image
void setImage( int hue, int colour, int contrast, int brightness,int whiteness, int depth )
{
	image.hue = hue;
	image.colour = colour;
	image.contrast = contrast;
	image.brightness = brightness;
	image.whiteness= whiteness;
	image.depth = depth;
	image.palette = PALETTE;
	
	if(ioctl(descrpFichierCam1, VIDIOCSPICT, &image)<0)
	{
   		perror("VIDIOCSPICT");
   		exit(-1);
	}
	printf("Donnee envoyees\n");
	printf("Hue: %d\n", image.hue);
	printf("Color: %d\n",image.colour);
	printf("Contrast: %d\n", image.contrast);
	printf("Brightness: %d\n", image.brightness);
	printf("Whiteness: %d\n", image.whiteness);
	printf("Depth: %d\n", image.depth);
	printf("Palette: %d\n", image.palette);
	printf("-------------------------------------------------------\n");
}

// procedure de modification des settings cam
void modify_cam_settings( int hue, int colour, int contrast, int brightness,int whiteness, int depth )
{
	printf("-------------------------------------------------------\n");
	printf("Parametres de CAMERA\n");

	getImage();
	setImage( hue, colour, contrast, brightness, whiteness, depth );
}

On remarque qu'on accède aux capacités de la cam avec la commande

ioctl(descrpFichierCam1, VIDIOCGCAP, &vidcap)

qui met à jour la structure vidcap, elle même définie dans linux/videodev.h
Toutes les autres structures travaillées avec ioctl sont définies dans ce fichier.
De même les paramètres de teinte, luminosité, contraste, balance des blancs et de profondeur sont accessibles en lecture avec

ioctl(descrpFichierCam1, VIDIOCGPICT, &image)

et en écriture avec

ioctl(descrpFichierCam1, VIDIOCSPICT, &image)

Récupération d’une image

Le code est un peu plus compliqué, il fait appel à la fonction mmap().
Je l’ai bien commenté donc il se passera d’autres explications.

void captureImageCam1(int type_capture, int rep)
{
	int i, nbcapture=0;
	int imageCam1=0;
	
	// la camera supporte-t-elle le VIDIOCGMBUF
	if(ioctl(descrpFichierCam1, VIDIOCGMBUF, &mbufCam1)<0)
	{
		perror("VIDIOCGMBUF");
		exit(-1);
	}
	ptrCam1 = (unsigned char*)mmap(0, mbufCam1.size, PROT_READ|PROT_WRITE, MAP_SHARED,descrpFichierCam1,0);
	if(ptrCam1==(unsigned char*) -1)
	{
		perror("mmap");
		exit(-1);
	}
	// Les demandes de captures sont mises dans la file d'attente du buffer
	mapbufCam1.height = HEIGHT ;
	mapbufCam1.width = WIDTH;
	mapbufCam1.format = PALETTE;

	// On boucle les demandes de captures selon le nombre de frames definies dans mbuf
	for(imageCam1=0; imageCam1 < mbufCam1.frames; imageCam1++)
	{
		mapbufCam1.frame = imageCam1;
		if(ioctl(descrpFichierCam1, VIDIOCMCAPTURE, &mapbufCam1))
		{
			perror("VIDIOCMCAPTURE");
			exit(-1);
		}	
	}
	imageCam1=0;
	while(nbcapture < rep)
	{
		i=-1;
		while(i < 0)
		{
			// Tant qu'on obtient une erreur, l'appel doit etre repete.
			// On verifie que la fonction a reussi et qu'il n'y a pas eu de signals qui ont interrompu l'appel
			i=ioctl(descrpFichierCam1, VIDIOCSYNC, &imageCam1);
			if(i < 0 || errno == EINTR)
			{
				if(i < 0)
				{
					perror("VIDIOCSYNC");
					printf("Le programme doit quitter\n\a");
					exit(-1);
				}
				continue;
			}
			break; // La sync. s'est bien deroule. On sort de la boucle
		}
		posImgCam1=ptrCam1+mbufCam1.offsets[imageCam1];
		// La fonction de traitement peut maintenant etre appellee avec posImg comme argument
		//printf("Adresse image CAMERA1: %p\n", posImgCam1);
		if ( type_capture == MAKE_PIXMAP_FILE )
		{
			// ici appel d'une fonction qui genere un fichier ppm
			extractPix(posImgCam1, HEIGHT,WIDTH);
		}
		else if ( type_capture == MAKE_OPENCV_OBJECT )
		{
			// ici passage au format opencv
			modify_opencv_cam1( posImgCam1, HEIGHT, WIDTH );
		}
		// On remet le buffer a la disposition du driver
		mapbufCam1.frame = imageCam1;
		if(ioctl(descrpFichierCam1, VIDIOCMCAPTURE, &mapbufCam1)<0)
		{
			perror("VIDIOCMCAPTURE");
			printf("L'application doit quitter\n\a");
			closeCam();
			exit(-1);
		}
		imageCam1++;
		if(imageCam1>mbufCam1.frames)
			imageCam1=0;
		nbcapture++;
	}
	// liberation du buffer
	munmap(ptrCam1, mbufCam1.size);
}

Bon maintenant nous savons configurer la webcam et récupérer des images, nous pouvons passer à la librairie opencv.

sommaire

Les fonctions et objets opencv

La documentation officielle d’opencv vous donnera beaucoup plus d’informations que moi mais je vais quand même faire un petit résumé des choses qui vont bien.
Pour aller plus loin et si vous avez installé le paquet de documentation rendez-vous en file:///usr/share/doc/opencv-doc/index.htm

La structure IplImage

C’est la structure de base pour stocker des images, elle contient de nombreuses informations sur l’image qui sont très pratiques en cours de code.
typedef struct _IplImage
{
        int  nSize;         /* sizeof(IplImage) */
        int  ID;            /* version (=0)*/
        int  nChannels;     /* Most of OpenCV functions support 1,2,3 or 4 channels */
        int  alphaChannel;  /* ignored by OpenCV */
        int  depth;         /* pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
                               IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported */
        char colorModel[4]; /* ignored by OpenCV */
        char channelSeq[4]; /* ditto */
        int  dataOrder;     /* 0 - interleaved color channels, 1 - separate color channels.
                               cvCreateImage can only create interleaved images */
        int  origin;        /* 0 - top-left origin,
                               1 - bottom-left origin (Windows bitmaps style) */
        int  align;         /* Alignment of image rows (4 or 8).
                               OpenCV ignores it and uses widthStep instead */
        int  width;         /* image width in pixels */
        int  height;        /* image height in pixels */
        struct _IplROI *roi;/* image ROI. when it is not NULL, this specifies image region to process */
        struct _IplImage *maskROI; /* must be NULL in OpenCV */
        void  *imageId;     /* ditto */
        struct _IplTileInfo *tileInfo; /* ditto */
        int  imageSize;     /* image data size in bytes
                               (=image->height*image->widthStep
                               in case of interleaved data)*/
        char *imageData;  /* pointer to aligned image data */
        int  widthStep;   /* size of aligned image row in bytes */
        int  BorderMode[4]; /* border completion mode, ignored by OpenCV */
        int  BorderConst[4]; /* ditto */
        char *imageDataOrigin; /* pointer to a very origin of image data
                                  (not necessarily aligned) -
                                  it is needed for correct image deallocation */
}
IplImage;

J’attire votre attention sur le fait que opencv est une librairie 32bits, lorsqu’elle travaille une image elle aime avoir des longueurs de ligne multiples de 32x3 (3 couleurs par pixel), si elle ne l’a pas elle complète sa ligne pour retomber sur ses pattes.
Je vous avertis avant que vous ne fassiez les mêmes bugs que moi (et que certaines fonctions internes)

Manipulation de IplImage

Allocation d’une image

iplImageBruteCam1 = cvCreateImage( cvSize(WIDTH,HEIGHT), IPL_DEPTH_8U, TRICANAL );

WIDTH et HEIGHT sont les même que dans la fonction de lecture d’image sur la camera. IPL_DEPTH_8U signifie que la profondeur de chaque canal est codée sur un entier non signé de 8 bits.
Ne faites jamais confiance à opencv sur les types signés ou non, vous aurez des surprises.

Création d’une fenêtre pour afficher son image

cvNamedWindow("Visicolors viewer",1);

Et oui, la librairie highgui (la copine de opencv) sait gérer des IHM !

Création d’une trackbar

cvCreateTrackbar("Rouge", "Analysis", &iRougeMoy, 255, NULL);


Envoyer son image dans la fenêtre

cvShowImage( "Analysis", iplImageMoy );


Convertir une image RBG en HLS

cvCvtColor( iplImageRoiCopied, iplImageHLS, CV_BGR2HLS );

Alors attention nativement une image n’est pas en RBG mais en BGR.

Attaquer proprement les valeurs des pixels

Le type IplImage vous donne accès aux pixels facilement. Chaque pixel est codé sur 3 char, soit 24bits (par défaut).
Il est possible d’y accéder directement

IplImage *img;

// acces au premier pixel
printf("Bleu=%d Vert=%d Rouge=%d, (int)img->imageData[0], (int)img->imageData[1], (int)img->imageData[2]);

Mais alors attention au problèmes de signe si vous faites des calculs sur les valeurs.
Une meilleure méthode d’accès est basée sur le type cvScalar

IplImage *img;
CvScalar value;
int x,y;  // coordonnees du pixel dans l'image

value = cvGet2D(img,x,y);

// plus 10 sur les bleus du pixel (x,y)
(unsigned)value.val[0] += 10;

cvSet2D(img,x,y,value);



sommaire

Le fonctionnement de visicolor

Je ne vais pas présenter le soft une fois de plus mais juste donner quelques billes pour le faire tourner chez vous et, avant, pour le compiler.
Vous devez avoir quelques *.h présents sur votre système pour que ça se passe bien, notemment :

#include "cv.h"
#include "highgui.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
52
De même le fichier camera.h vous permettra de configurer deux ou trois bricoles

//le fichier externe pour y mettre l'image sauvegardee au lancement (pensez à mettre un chemin dans lequel vous avez les droits)<
#define EXTERNAL_FILE		"camera.ppm"		
// device configuration
#define CAM1_DEVICE			"/dev/video0"		// point de montage de la CAM1
// TRES IMPORTANT A REGLER SUIVANT VOTRE CAMERA !!!
#define WIDTH					352			// resolution X des cam
#define HEIGHT				288			// resolution Y des cam
#define IMAGE_DATA_SIZE	3*WIDTH*HEIGHT
#define LINE_DATA_SIZE	3*WIDTH
#define PALETTE	VIDEO_PALETTE_RGB24;

#define XCENTER	WIDTH/2	// centre de l'image
#define YCENTER	HEIGHT/2

#define CENTER_ROI_DIM	12	// taille en X et Y de la pastille

// commandes de captureImageCam
#define MAKE_PIXMAP_FILE			1
#define MAKE_OPENCV_OBJECT		2
#define DO_NOTHING					0

// opencv configuration (vous pouvez laisser)
#define HUIT_BITS				8
#define MONOCANAL			1
#define TRICANAL				3
#define ROUGE				1
#define VERT					2
#define BLEU					3
#define AUTOSIZE				1

// default cam settings (faudra surement y toucher)
#define DEFAULT_HUE				0
#define DEFAULT_COLOUR		0
#define DEFAULT_CONTRAST		11264
#define DEFAULT_BRIGHTNESS	27136
#define DEFAULT_WHITENESS		0
#define DEFAULT_DEPTH			24


Donc voilà, vous pouvez y aller, soit coder en reprenant visicolor comme base (vous pouvez y mettre du GTK2.0 directement), soit coder à partir de vi, soit le lancer et voir ce que ça raconte.
Juste pour vous montrer, voici une paire de capture d’écran.

capture ecran 1

Un rouge est bien rouge.

capture ecran 2

Un vert est bien rouge aussi.

Conclusion : il va falloir calibrer la caméra.
Dans mon cas avec ma QuickCam Chat, je double la quantité de bleu pour récupérer une image correcte. Vous pourrez faire des essais en pointant sur des plans de couleur connue et en comparant les valeurs de teinte (HUE). Le rouge est en 0 et 180, le vert en 60 et le bleu en 120. Vous pouvez aussi regarder les niveaux RGB relatifs mais ils sont fortement influencés par la lumière alors que la teinte non.


sommaire

Visicolor 0.2

J'ai apporté pas mal de modifications par rapport à la première version, par exemple :

sommaire


Valid HTML 4.01 Transitional