COMPUTER VISION I

tutorial de programmation
en processing

 

 

Computer vison

dessein
Voici une première description de la computer vision:
CV is the science and technology of machines that see. As a scientific discipline, computer vision is concerned with the theory for building artificial systems that obtain information from images. The image data can take many forms, such as a video sequence, views from multiple cameras, or multi-dimensional data from a medical scanner.

As a technological discipline, computer vision seeks to apply the theories and models of computer vision to the construction of computer vision systems. Examples of applications of computer vision systems include systems for:

source : http://en.wikipedia.org/wiki/Computer_vision

La vision par ordinateur est aussi aujourd'hui un outil et sujet de nombreux oeuvres artistiques. Depuis des pionniers tels que Myron Krueger ou David Rokeby dans les années 80, la CV est utilisé dans des installations, performances, créations chorégraphiques et théâtrale.

Dans ce cours, nous utilisons la bibliothèque OpenCV pour l'instant juste pour récupérer une images de la webcam afin d'appliquer quelques algorithmes simples de la computer vision

Lecture de la webcam

La lecture d'un webcam avec OpenCV est quasi la même procédure que la lecture d'un vidéo

import hypermedia.video.*;

OpenCV monOpenCV

void setup(){
size(320,240);
monOpenCV= new OpenCV(this);

monOpenCV.capture(width, height);
// allouer la mémoire pour la capture de vidéo
}

void draw() {
monOpenCV.read();
image( monOpenCV.image(), 0, 0 );
}

Le pixlel le plus lumineux

L'un des principales taches de la 'computer vision' est le traitement d'image (image processing). Le but du traitement d'image est la transformation de l'image ou d'une partie permettant d'aller vers une interprétation des images traitées. Différents algorithmes ou opérateurs de traitement d'image nous permet d'extraire des informations pertinents. Une des ces opérateurs est le 'pixel à pixel' (aussi appelés opérateurs point à point). Voici un algorithme simple pour extraire le pixel le plus lumineux.

Pour aller plus loin / traitement image: http://fr.wikipedia.org/wiki/Traitement_d%27images

import hypermedia.video.*;
OpenCV monOpenCV;

void setup() {
size(320, 240);
monOpenCV = new OpenCV(this);
monOpenCV.capture (width, height);
noStroke();

void draw() {
monOpenCV.read();
image(monOpenCV.image(), 0, 0);
// affiche la webcam
loadPixels();
// charge la liste de pixels qui sont actuellement sur l'écran
int brightestX = 0;
// variable pour l'X-coordonée du pixels le plus lumineux
int brightestY = 0;
// et Y-coordonée du pixels le plus lumineux
float brightestValue = 0;
// la valeur de "brightness " du pixels le plus lumineux
//regarde chaque pixel de la liste et prend sa couleur
for (int i = 0; i < pixels.length; i++) {
color pixelValue = pixels[i];
// Détermine la "brightness" du pixel
float pixelBrightness = brightness(pixelValue);
// si la "brightness" valeur du pixel actuel est plus grand que le plus grand,
//prend lui comme plus grand et convertit sa position dans la liste en coordonnée x, y
if (pixelBrightness > brightestValue) {
brightestValue = pixelBrightness;
brightestX = i%width;
brightestY = (i-brightestX)/width;
}
}
updatePixels();
// update les pixels dans la matrix
fill(255, 204, 0, 128);
ellipse(brightestX, brightestY, 20, 20);
//dessine un ellipse a la position du pixel le plus lumineux
}

Filtre II Threshold

Le traitement d'image par seuil (thresholding image processing) est un groupe d'algorithme pour segmenter des images digitales. L'exemple, précédent a été déjà un premier cas de segmentation. On découpe le groupe de pixel en deux segments, le groupe du pixel le plus lumineux et les autres.

La segmentation est un étape important dans l'analyse de l'image, pour par exemple reconnaître des objets. Avec l'aide du seuilage on peut distinguer un objet de son environnement. Le traitement par seuil produit des images binaires.

Voici un autre algorithme simple de binarisation d'image qui transforme l'image RGB en image binaire N/B

import hypermedia.video.*;

OpenCV monOpenCV;
int threshold=127;
void setup() {
size(320, 240);
monOpenCV = new OpenCV(this);
monOpenCV.capture (width, height);
}

void draw() {
monOpenCV.read();
monOpenCV.convert( OpenCV.GRAY );
// transforme l'image en N/B
image(monOpenCV.image(), 0, 0);
// affiche la webcam

loadPixels();
// charge la liste de pixels qui sont actuellement sur l'écran
for (int i = 0; i < pixels.length; i++) {
//regarde chaque pixel de la liste
float pixelValue =red(pixels[i]);
// et prend sa couleur, dans 'GRAY' les valeurs de R+G+B sont pareils
if (pixelValue>threshold)pixelValue=255;
// décide si le pixel devient noir ou blanc
else pixelValue =0;
pixels[i]=color(pixelValue);
}
updatePixels();
// update les pixels dans la matrix
fill(255, 204, 0, 128);
ellipse(brightestX, brightestY, 20, 20);
//dessine un ellipse à la position du pixel le plus lumineux
}

OpenCV nous propose déjà une série de seuilages d'images. La méthode la plus simple est threshold (int valeur<255). L'argument indique le seuil ou le pixel devient noir ou blanc, comme dans l'exemple au dessus. Si il s'agit d'un image en RGB, chaque composant devient soit 0 soit 255, c-a-d l'image se transforme en couleurs premières et secondaires (R, G, B, R+G, G+B...).

Il y a d'autres façon plus sophistiqué qui exige trois arguments

threshold(value, max, type);
value = le valeur du threshold <255,
max= la valeur qui prend les pixels au delà du seuil. Si j'indique 200, l'image se décompose en deux couleur 0 et 200.
type= Il y a plusieurs type qui sont décrit ci-dessus

exemple
monOpenCV.threshold(127, 200, OpenCV.THRESH_TOZERO_INV);

CV_THRESH_BINARY:
dst(x,y) = max_value, if src(x,y)>threshold
0, otherwise

CV_THRESH_BINARY_INV:
dst(x,y) = 0, if src(x,y)>threshold
max_value, otherwise

CV_THRESH_TRUNC:
dst(x,y) = threshold, if src(x,y)>threshold
src(x,y), otherwise

CV_THRESH_TOZERO:
dst(x,y) = src(x,y), if src(x,y)>threshold
0, otherwise

CV_THRESH_TOZERO_INV:
dst(x,y) = 0, if src(x,y)>threshold
src(x,y), otherwise

*src=pixel de l'image source
dst=pixel de l'image après transformation

threshold graphique

Il y a un type supplémentaire qui s'appelle OTSU, nommé d'après Nobuyuki Otsu qui a développé un algorithme en 1979 pour trouver le "threshold' optimal dans un image. Il fait recours aux méthodes de la statistique et de la variance. Si on utilise 'OTSU' dans la méthode threshold(), le 'value' doit être renseigné, mais il sera calculé par OpenCV.
Pour aller plus loin avec Otsu =>

exemple
monOpenCV.threshold(127, 200, OpenCV.THRESH_TOZERO_INV + OpenCV.THRESH_OTSU);

 

Documentation OpenCV/Processsing: threshold()
DocumentationOpenCV http://opencv.willowgarage.com/wiki/CvReference#FiltersandColorConversion
( descend jusqu'a threshold)
en anglais http://en.wikipedia.org/wiki/Thresholding_(image_processing)
en francais http://www.siteduzero.com/tutoriel-3-8607-binarisation-et-images-binaires.html

 

Soustraction du fond

Voici l'algorithme qui révèle la différence entre deux images, n et n-1(l'image dans le buffer). Il y a également la fonction absDiff() qui fait la même tache, l'avantage de l'algorithme est que je maîtrise chaque pixel et je peux mesurer la 'grandeur' du changement.

import hypermedia.video.*;

OpenCV monOpenCV;
color bkgdPixels[];
// liste mémoire pour les pixels
void setup() {
size(320/2, 240/2);
monOpenCV = new OpenCV(this);
monOpenCV.capture (width, height);
bkgdPixels= new color[width*height];
}

void draw() {
monOpenCV.read();
monOpenCV.convert(OpenCV.GRAY);
// transformer l'image en N/B
image(monOpenCV.image(), 0, 0);
bkgdSubstraction();
// appelle la fonction de soustraction
}

void keyPressed() {
loadPixels();
arraycopy(pixels, bkgdPixels);
// copie les pixels de l'image dans le mémoire
}

void bkgdSubstraction(){
loadPixels();
// charge la liste de pixels qui sont actuellement sur l'écran
int diffTotal=0;
// variable pour stocker la différence entre les pixels
for (int i = 0; i < pixels.length; i++) {
// examiner pixel par pixel
float currR=red(pixels[i]);
// la valeur du rouge du pixel actuel
// float currG=green(pixels[i]);
// desactivé si on travail on Gray
// float currB=blue(pixels[i]);

float bkgdR=red(bkgdPixels[i]);//la valeur du rouge du pixel du memoire
// float bkgdG=green(bkgdPixels[i]);/
/ desactivé si on travail on Gray
// float bkgdB=blue(bkgdPixels[i]);

float diffR = abs(currR - bkgdR);// calculer la différence entre pixel mémoire et actuel
// float diffG = abs(currG - bkgdG);
// float diffB = abs(currB - bkgdB);

// diffTotal+= diffR + diffG + diffB;
//println(diffTotal);
// println(diffR);
// pixels[i] = color(diffR, diffG, diffB);
pixels[i] = color(diffR);
// actualiser la liste des pixels avec la couleur de soustraction
}
updatePixels();
// update les pixels dans la matrix
}

 

Exercice