Tutorial ScanLine
<< Par Bacterius >>
1) Présentation
ScanLine est une fonction présente dans chaque objet TBitmap.
C'est de loin la façon la plus rapide
d'accéder aux pixels du bitmap.
Mais attention : certains préréquis sont nécessaires pour pouvoir utiliser cette fonction ... 2) Préréquis
- il faut déclarer des types, contenus par défaut dans l'unité de Windows, mais ne sont pas déclarés par défaut. Dans une clause "type", dans l'interface de votre unité, déclarez ces 2 types :
TRGBArray = array [0..10000] of TRGBTriple;
pTRGBArray = ^TRGBArray;
Nous allons décortiquer ces 2 lignes :
TRGBArray
: il s'agit du nom (vous pouvez le modifier mais autant le garder come cela) par lequel vous définirez une ligne ScanLine. Elle ne sera jamais utilisée dans votre code directement. Array[0.10000] of
: l'étendue maximale d'une ligne ScanLine. Ici, le maximum est de 10.000 pixels (c'est suffisant je pense, mais à adapter aux besoins de chacun). TRGBTriple
: c'est le type déclaré dans l'unité de Windows, il représente un enregistrement RGB (Red, Green, Blue). Son enregistrement complet est :
TRGBTriple=record
rgbtBlue: Byte;
rgbtGreen: Byte;
rgbtRed: Byte;
end;
pTRGBArray
: Il s'agit en fait de l'équivalent pointeur de TRGBArray. Il sera, en quelque sorte, le porte-parole de TRGBArray. Vous pouvez changer le nom de ce type, mais il est conseillé de le garder comme tel (il faudra l'appeler dans le code). ^TRGBArray
: L'equivalent pointeur, graçe au ' ^ '. - vous devez avoir un bitmap de type Pf24Bit (expliqué plus tard dans le tutorial). Pour cela, il faudra, avant chaque tentative d'accès à ScanLine, appliquer cette ligne :
MonBitmap.PixelFormat := pf24Bit;
3) Explications sur ScanLine
Bon, j'ai dû vous le dire, ScanLine est la façon la plus rapide d'accéder aux pixels du bitmap. Cela vient du fait qu'il ne nécessite aucune copie en mémoire des pixels, car le pointeur attribué par ScanLine (pTRGBArray) reflète directement l'état des pixels du bitmap, contrairement à Pixels[X, Y], ou à la paire GetDiBits, SetDiBits.
Ne parlons pas de Pixels[X, Y], le petit chouchou de ceux qui n'ont jamais essayé ScanLine : chaque pixel doit être scanné et mis en mémoire indépendemment, d'ou la longueur du traitement.
Le GetDiBits, SetDiBits est beaucoup plus rapide que Pixels[X, Y], mais est toujours largement battu par ScanLine.
GetDiBits et SetDiBits
sont des routines API. GetDiBits scanne le pixel, en récupérant les valeurs RGB, et SetDiBits les applique sur le bitmap passé en paramètre. Le traitement des pixels doit se situer entre GetDiBits et SetDiBits. Mais c'est un tutorial pour ScanLine, et je ne m'attarderai pas sur ces routines.Remarque : ScanLine scanne les lignes horizontalement, donc il faut toujours faire un parcours des pixels de cette façon : for
A := 0 to Height - 1 do for B := 0 to Width - 1 do
4) Exemple d'utilisation de ScanLine
Ici l'on construira une routine pour appliquer un filtre rouge sur le bitmap.
procedure
FiltreRouge(Bitmap: TBitmap); Var
P: PTRGBARRAY;
// Le pointeur ScanLine, qui sera appelé à chaque ligne. X, Y: Integer;
// Les variables de contrôle for-do, pour parcourir le bitmap Bmp: TBitmap;
// Le bitmap tampon pour effectuer les traitements begin
Bmp := TBitmap.Create; // Création du bitmap tampon
Bmp.Assign(Bitmap);
// Le bitmap tampon récupère le contenu du bitmap passé en paramètre Bmp.PixelFormat := pf24Bit;
// On change les formats des pixels du bitmap for X := 0 to Bmp.Height - 1 do
// On scanne chaque ligne du bitmap, et on scanne cette ligne avec ScanLine begin
P := Bmp.ScanLine[X];
// On scanne la ligne ... for Y := 0 to Bmp.Width - 1 do
// Pour chaque pixel de la ligne ... begin
P[Y].rgbtGreen := 0;
// Le pixel actuel de la ligne (contenue dans P) fixe sa valeur G à 0 P[Y].rgbtBlue := 0;
// Même chose pour la valeur B end;
// Donc, si l'on fixe les valeurs G et B à 0, il reste les valeurs R (Red, Rouge), qui sont restées intactes, et on obtient un bitmap ... end;
// ... avec des niveaux de rouge (comme si l'on avait appliqué un cellophane rouge dessus) Bitmap.Assign(Bmp);
// Ne pas oublier de redonner l'image traitée au bitmap original Bmp.Free;
// On libère le bitmap tampon end;
5) Précisions supplémentaires sur ScanLine
Les valeurs RGB (rgbtRed, rgbtGreen, rgbtBlue) sont comprises entre 0 et 255.
Si vous dépassez 255, la valeur passera immédiatement à 0 (Réciproquement, si vous allez en dessous de 0, il passera automatiquement à 255).
Pour éviter cela, il est conseillé d'utiliser des variables R, G, B (Integer) pendant le traitement, de vérifier les étendues, de les remettre à 255 si elles dépassent 255 (ou 0 si elles sont inférieures de 0), et de les restituer à ScanLine ensuite.
Pensez bien à entourer votre ScanLine de try..except, car les exceptions sont fréquentes (Indice de ligne hors limites notamment).
ScanLine,
avec son type par défaut , ne prend pas en charge les octets de transparence contenus dans les bitmaps 32 bits. Passons à la loupe un pixel de bitmap32 bits :
rgbtRed: Byte;
// Octet (8 bits) valeur rouge rgbtGreen: Byte;
// Octet (8 bits) valeur verte rgbtBlue: Byte;
// Octet (8 bits) valeur bleue rgbtAlpha: Byte;
// Octet (8 bits) valeur alpha (degré de transparence)
Mais rien ne vous empêche de coder un ScanLine avec prise en charge Alpha ;)
Il suffit de faire un enregistrement pointeurcompatible avec ScanLine (voir 1er lien externe)
Vous pouvez absolument tout faire avec ScanLine sur les valeurs rouge, vert, bleu, et Alpha (mais le type fourni par défaut est limité à rouge, vert, bleu).
6) Tests de performances
Afin d'illustrer la rapidité de ScanLine comparé aux autres routines de traitement de pixels, voici un test de performance !
[ Tests effectués en parfaites conditions de performance, avec un processeur Intel(R) Celeron(R) M, 1.50 GHz ]
[ Bitmap testé : dimensions : 255 x 255 pixels, profondeur de couleur 24 bits ]
[ Traitements effectués : ajout de 10 aux valeurs rouges, 5 aux valeurs vertes, 3 aux valeurs bleues ]
[ Le test de performance prend en compte la mise en mémoire du bitmap tampon, sa création et sa libération, et l'affectation depuis, et à un bitmap paramètre ]
Préliminaires (aucune valeur, mise en chauffe du processeur) :
ScanLine : 0,6 MS
Pixels : 531 MS
GetDiBits/SetDiBits : 155 MS
Test 1 :
ScanLine : 0,4 MS
Pixels : 453 MS
GetDiBits/SetDiBits : 182 MS
Test 2 :
ScanLine : 0,5 MS
Pixels : 665 MS
GetDiBits/SetDiBits : 123 MS
Test 3 :
ScanLine : 0,5 MS
Pixels : 438 MS
GetDiBits/SetDiBits : 244 MS
Moyenne des performances :
ScanLine : (0,4 + 0,5 + 0,5) / 3 = 0,46 MS << 1ere place >>
GetDiBits/SetDiBits : (182 + 123 + 244) / 3 =
183 MS << 2eme place >> Pixels : (453 + 665 + 438) / 3 = 518,6 MS << 3eme place >>
Vous pouvez maintenant constater la rapidité écrasante de ScanLine par rapport aux autres routines ...
Alors ...
Faites le bon choix, faites le choix
ScanLine !
7) Liens externes
http://www.efg2.com/Lab/ImageProcessing/Scanline.htm (Exemples d'utilisation de ScanLine, en anglais)
http://dn.codegear.com/article/29173 (Article CodeGear sur ScanLine)
Bon, j'ai essayé d'écrire un tutorial interessant, et agréable à lire. Il manque peut-être des informations ... C'est mon 2eme tutorial sur CS :)
Cordialement, Bacterius !