Traitement d'images avec Delphi
SOMMAIRE
I Introduction
II Comment récupérer les valeurs RGB de chaque pixel contenu dans l’image ?
III Conversion d’une image couleur en niveaux de gris
IV Définition d'un histogramme
V Création d’un histogramme avec Delphi
I Introduction
Dans ce premier tutoriel consacré au traitement d’images, je vais vous montrer comment utiliser les fonctions Delphi pour réaliser des fonctions de traitement d’images. Nous allons dans un premier temps, voir comment il est possible de récupérer la valeur des pixels, et de les traiter. Nous verrons ensuite commet réaliser un histogramme sur une image en niveau de gris.
II Comment récupérer les valeurs RGB de chaque pixel contenu dans l’image ?
Tout d’abord tout ce dont on a besoin pour lire le contenu d’une image se trouve dans l’unité Windows. Il faut donc rajouter dans la partie uses la référence à cette unité.
Uses …, Windows, …;
Maintenant nous allons pouvoir tout mettre en œuvre pour lire chaque pixel. Pour ce faire on a besoin d’un type de données qui va pointer sur chaque pixel et qui va nous retourner sa valeur RGB. C’est-à-dire un triplet de valeur correspondant à ses valeurs en rouge, vert et bleu :
pRGBTripleArray = ^TRGBTripleArray;
TRGBTripleArray = ARRAY[0..PixelCountMax-1] of TRGBTriple;
Ces valeurs vont être stockées dans un tableau. Pour créer un tableau de type pRGBTripleArray, il faut déclarer la ligne suivante :
HistogramTab : pRGBTripleArray;
Pour lire les informations contenues dans une image il faut utiliser l’algorithme suivant :
Pour i := HauteurImage – 1 jusqu’à 0 faire
Debut
Lire une ligne de pixels
Fin
Cet algorithme suffit pour récupérer dans un tableau ce que nous voulons.
Il faut que l’on crée un tableau de type pRGBTripleArray qui va reccueillir :
La fonction qui permet de lire une ligne de pixels à partir d’une image est Scanline
Cette fonction fait partie de la bibliothèque de fonctions Graphics
Elle s’utilise de la manière suivante :
Row := Bitmap.scanLine[j];
Cette opération est simple, mais elle est le point de depart de tout traitement d’images. Nous verrons par la suite que les choses se compliquent. D’ailleurs nous allons étudier la création d’un histogramme à partir d’une image en niveau de gris.
III Conversion d’une image couleur en niveaux de gris
Cette opération consiste à convertir chaque intensité de couleur de pixel en niveaux de gris. Il existe 256 niveaux de gris. Donc pour obtenir un niveau de gris à partir d’un triplet RGB vous devez effectuer l’opération suivante :
nvGris := (RgbtRed + RgbtGreen + RgbtBlue) DIV 3;
L’algorithme de cette opération devient le suivant :
Pour i := HauteurImage – 1 jusqu’à 0 faire
Debut
Lire une ligne de pixels dans tableau tabRGB
Lire une ligne de pixels dans tableau tabGray
Pour j:= LargeurImage – 1 jusqu’à 0 faire
Debut
Pour chaque pixel
nvGris := (indexRouge+indexVert+indexBleu) DIV 3
//affecter aux composantes du tableau tabGray le niveau de gris calculé
RgbtRed:=nvGris
RgbtGreen:=nvGris
RgbtBlue:=nvGris
Fin
Fin
Voici le code correspondant :
procedure TFrmImageFile.ConversionnvGris;
var
Gray : INTEGER;
i : INTEGER;
j : INTEGER;
rowRGB : pRGBTripleArray;
rowGRAY: pRGBTripleArray;
begin
Screen.Cursor := crHourGlass;
Bitmap.PixelFormat := pf24Bit;
TRY
for j := Bitmap.Height - 1 downto 0 do
begin
rowRGB := Bitmap.Scanline[j];
rowGRAY := Bitmap.ScanLine[j];
for i := Bitmap.Width-1 downto 0 do
begin
With rowRGB[i] do
Gray := (RgbtRed + RgbtGreen + RgbtBlue) DIV 3;
With rowGray[i] do
begin
RgbtRed := Gray;
RgbtGreen := Gray;
RgbtBlue := Gray;
end;
end;
end;
finally
Screen.Cursor := crDefault;
end;
Image1.Picture.Graphic := bitmap;
end;
Cette opération va nous permettre maintenant d’étudier dans la partie suivante de ce tutoriel la création de l’histogramme d’une image en niveau de gris.
IV Définition d’un histogramme
L’histogramme d’une image est une fonction donnant, par exemple, le nombre de pixels à un niveau de gris particulier en fonction du niveau de gris.
Un histogramme peut-être vu comme une fonction de densité de probabilité discrète pour une image individuelle dans le sens suivant : Chaque essai pour l’expérience de probabilité est la sélection d’un pixel au hasard à partir de l’image, et l’événement mesuré est le niveau de gris du pixel choisi.
Quand la fonction de densité de probabilité mesurée pour chaque niveau de gris est multipliée par le nombre de pixels dans l’image, les valeurs obtenues sont celles de l’histogramme.
L’histogramme ne contient aucune information relative à l’emplacement des pixels ni sur la proximité relative de deux pixels. Par contre, l’information qu’il contient peut concerner notamment la brillance apparente et le contraste d’une image, et il est utilisé en traitement d’images pour manipuler ces caractéristiques d’une image.
Une fois que l’histogramme d’une image est connu, les niveaux de gris de l’image peuvent être manipulés pour changer l’histogramme de la manière souhaité. On peut par exemple souhaiter améliorer le contraste, changer le niveau de brillance ou faire correspondre l’histogramme à celui d’une autre image. On rappelle que toute technique de modification de niveaux de gris, dont la modification d’histogramme est un exemple, est basée sur la création d’une correspondance (« mapping ») en anglais) entre les niveaux de gris de l’image originale et les niveaux de gris correspondant dans l’image modifiée.
Dans des applications limitées, l’analyse d’histogramme pour déterminer des seuils a aussi été utilisée. L’histogramme est calculé pour les valeurs d’intensité de l’image et analysé pour déterminer l’établissement d’un seuil permettant de séparer un objet du fond. Souvent les objets, cellules, chromosomes, …, sont tels qu’ils ont des valeurs caractéristiques très différentes de celles du fond. Ce type d’analyse est applicable dans ces cas plus généraux qu’un initialement envisagée.
V Création d’un histogramme avec Delphi
Nous allons prendre, comme base, la technique de base que l’on a énoncé au chapitre précédent, concernant la récupération des valeurs des pixels dans un tableau. En effet dans le cas présent, il va falloir remplir un tableau à deux dimensions ImageMap[x, y] , qui va nous permettre de récupérer les valeurs d’intensité en niveaux de gris de chaque pixel, Ce tableau va nous permettre ensuite de . Il sera ensuite possible de réaliser certain s traitements de base sur l’histogramme lui même, mais ce ne sera pas abordé dans ce tutoriel. L’algorithme que l’on va utiliser pour l’établissement de l’histogramme est le suivant :
Pour i := LargeurImage – 1 jusqu’à 0 faire
Début
Lire une ligne de pixels
Pour j := LongueurImage jusqu’à 0 faire
Début
Pour chaque pixel faire
ImageMap[j, i] := valeur intensité rouge
index := valeur intensité rouge
HistTab[index] := HistTab[index] + 1
Tester ImageMap [j,i] si supérieur à MaxColor
Tester ImageMap [j,i] si inférieur à MinColor
Fin
Fin
Voici donc les points les plus importants qui vont nous permettre de réaliser la création d’un histogramme. Maintenant passons à la programmation de ce petit algorithme. Il faut d’abord déclarer un tableau ImageMap dynamiquement et l’initialiser :
Var ImageMap : array of array of byte ;
Le type byte nous suffit, puisque nous avons que 256 niveaux de gris dans une image. Et l’initialisation se fait comme suit :
Setlength(ImageMap, ImageColumns, ImageArrows) ;
La variable index reçoit la valeur d’intensité rouge du pixel. En effet pour une image en niveau de gris, les valeur d’intensité rouge, verte et bleue sont identiques. Donc on a :
index := row[j].rgbtRed;
et on a aussi :
inc(HistTab[index]) ;
ceci permet d’incrémenter d’une unité le nombre de pixels correspondant à l’index de niveau de gris. Maintenant que l’opération de remplissage du tableau HistTab est réalisé, il vous reste à réaliser l’opération d’affichage graphique de ces valeurs. Je vous laisse réaliser cette opération à votre convenance. Il est de plus possible de réalisé des opérations sur cet histogramme comme le moyennage, mais nous n’en parlerons pas dans ce tutoriel, le but de celui-ci est de vous donner les bases de programmation concernant le traitement des images.
Je vous donne le code complet de la procédure InitialiseTab :
procedure THistogram.InitialiseTab;
var i, j : integer;
index : byte;
Row : pRGBTripleArray;
begin
SetLength(ImageMap, TabColumns, TabArrows);
for j := TabArrows - 1 downto 0 do
begin
Row := Bitmap.scanLine[j];
for i := TabColumns - 1 downTo 0 do
begin
with row[i] do
begin
ImageMap[i][j] := rgbtRed;
index := rgbtRed;
end;
GetHistogramTab(index);
if ImageMap[i][j] > MaxColor then
MaxColor := ImageMap[i][j];
if ImageMap[i][j] < MinColor then
MinColor := ImageMap[i][j];
end;
end;
end;
Et maintenant voici la procédure GetHistogramTab :
procedure THistogram.GetHistogramTab(index: byte);
var i, j : integer;
begin
inc(HistTab[index]);
end;
Et maintenant, voici voici la procédure qui dessine l'histogramme :
procedure THistogram.Paint;
var i : integer;
begin
inherited Paint;
HauteurGraphMax := Height ;
EchelleY := HauteurGraphMax / GetMaxCount;
with Canvas do
begin
Pen.Color := ClBlack;
Brush.Color := ClWhite;
FillRect(Rect(0, 0, Width, Height));
MoveTo(16, 10);
LineTo(16, Round(GetMaxCount * EchelleY));
LineTo(MaxColor+16, Round(GetMaxCount * EchelleY));
for i := 0 to GetMaxCount do
begin
if ((i mod 50) = 0) then
TextOut(0, Round((GetMaxCount * EchelleY) - (i * EchelleY)),
IntTostr(i));
end;
//Mise en place des légendes du graphe
TextOut(0, Round(GetMaxCount * EchelleY)-16, '0');
TextOut(0, 0, IntToStr(GetMaxCount));
TextOut(MaxColor, Round(GetMaxCount * EchelleY)-16, IntToStr(MaxColor));
TextOut(20,10,'Statistique :');
TextOut(20,30,'Valeur Min :' + IntTostr(GetMinCount) + ' Nv. Gris :' +
IntTosTr(AbsMinColor));
TextOut(20,50,'Valeur Max :' + IntTostr(GetMaxCount) + ' Nv. Gris :' +
IntTosTr(AbsMaxColor));
end;
for i := 0 to MaxColor do
begin
Canvas.MoveTo(16 + i, Round(GetMaxCount * EchelleY)-16);
Canvas.LineTo(16 + i, Round((GetMaxCount * EchelleY) -
(HistTab[i] * EchelleY))-16);
end;
end;