Utilisation de LibTiff avec Delphi
Date de publication : 25/05/2005 ,
Date de mise a jour : 28/05/2005
Par
Eric SIBERT (Site)
Exemple d'utilisation de LibTiff avec Delphi : réduire en 256 couleurs optimales une grande image Tiff
I. Introduction
II. Détermination de la palette
III. Dans le code
IV. Conclusion
I. Introduction
Le format d'image Tiff est assez répandu et on peut trouver des composants Delphi pour lire voir écrire des images à ce format.
Toutefois, dans certains cas, comme la manipulation de grandes images, on peut vouloir accéder directement au contenu de l'image
sans la charger entièrement en mémoire comme le fait un composant.
Il convient alors d'utiliser des outils de plus bas niveau.
Compte tenu du nombre de variantes que comprend le format, il n'est pas envisageable d'aller lire directement le fichier, contrairement au Bitmap par exemple.
Il existe par contre une librairie adaptée à ce type de travail : LibTiff.
Cette librairie est disponible en opensource ... et en C.
Heureusement, Joris Van Damme
a réalisé une traduction des entêtes de LibTiff pour Delphi.
Dans la suite de cet article, je vais vous montrer comment se servir de cette librairie pour
réduire de 16 millions à 256 le nombre de couleurs d'une image à priori grande.
En essayant d'obtenir une palette de bonne qualité.
Cette opération s'appelle aussi une quantification de palette.
II. Détermination de la palette
Sur le principe de base, il y a un article chez Microsoft.
Grosso modo, dans l'espace 3D des couleurs, on constitue des boîtes.
Une boîte est définie par ses limites RougeMin, RougeMax, VertMin, VertMax, BleuMin, BleuMax.
Chaque boîte contient une partie des couleurs présentes dans l'image avec leur fréquence d'apparition.
Au début, on a une seule boîte englobant l'ensemble des couleurs.
On recherche le plus grand côté de la boîte.
On découpe la boîte perpendiculairement à cette direction, au niveau de la valeur moyenne des couleurs qu'elle contient.
Par exemple, on constate que la boîte contient des rouges de 3 à 249, des bleus de 25 à 230 et des verts de 50 à 240.
Le rouge constitue le plus grand côté.
On trouve par ailleurs que la valeur moyenne des rouges contenus dans la boîte est de 118,3.
On va découper la boîtes en deux boîtes plus petites, l'une avec les rouges de 3 à 118 et l'autre de 119 à 249.
Puis on recommence l'opération sur les nouvelles boîtes. On cherche sur l'ensemble des boîtes celle qui est la plus allongée.
Et on la découpe suivant le même principe.
On répète l'opération jusqu'à avoir autant de boîtes que de couleurs voulues (256 ou 236 ... ou 23).
Chaque boîte représente une entrée dans la palette.
On prend la valeur moyenne des pixels de la boîte pour définir la couleur de la palette.
Il ne reste plus qu'à parcourir l'image et à déterminer pour chaque pixel dans quelle boîte il se trouve.
Le numéro de la boîte est alors le numéro dans la palette.
Le pixel qui est dans la boîte 53 aura la couleur 53 de la palette.
A chaque fois qu'on veut découper une boîte, il faut déterminer la valeur moyenne de la couleur correspondante.
Par exemple, en parcourant l'image et en repérant les pixels dont la couleur est dans la boîte puis en les ajoutant au calcul de la moyenne.
A répéter par le nombre de couleur qu'on veut obtenir. Ce qui pour une image importante, voir stockée sur un disque dur, risque d'être très long.
Pour contourner ce problème, j'ai fait un tableau avec les 16 millions de couleurs possibles et je ne parcours l'image qu'une fois pour noter la fréquence d'apparition de chaque couleur.
J'utilise ensuite ce tableau pour faire les calculs sur les boîtes.
L'allocation d'un tableau de 16 millions d'entiers (64 Mo) n'est certainement pas optimale pour une petite image.
La méthode ne devient intéressante que quand la taille de l'image dépasse celle du tableau.
III. Dans le code
Après avoir téléchargé la librairie, on l'appelle dans notre projet :
uses
LibTiffDelphi;
Pour la lecture d'une image Tiff :
var
Source : PTiff;
BitsPerSampleValue: Word;
Width, Height : Cardinal;
SourceLine, DestLine : array of byte;
I, J : integer;
begin
Source:=TIFFOpen(FileName,'r');
assert(Source<>nil,'Source File Missing');
try
assert(TIFFGetField(Source,TIFFTAG_BITSPERSAMPLE,@BitsPerSampleValue)=1,
'Corrupted Source File with TIFFTAG_BITSPERSAMPLE');
assert(BitsPerSampleValue=8,'Source File is not a 8bit color depth');
assert(TIFFGetField(Source,TIFFTAG_SAMPLESPERPIXEL,@BitsPerSampleValue)=1,
'Corrupted Source File with TIFFTAG_SAMPLESPERPIXEL');
assert(BitsPerSampleValue=3,'Source File as not 3 samples per value');
assert(TIFFGetField(Source,TIFFTAG_IMAGEWIDTH,@Width)=1,'Corrupted Source file with TIFFTAG_IMAGEWIDTH');
assert(TIFFGetField(Source,TIFFTAG_IMAGELENGTH,@Height)=1,'Corrupted Source file with TIFFTAG_IMAGELENGTH');
setLength(SourceLine,3*Width);
FillChar(Density[0,0,0],16777216,0);
for J:=0 to Height-1 do
begin
TIFFReadScanline(Source,@(SourceLine[0]),J,0);
for I:=0 to Width-1 do
inc(Density[SourceLine[3*I],SourceLine[3*I+1],SourceLine[3*I+2]]);
end;
[...]
finally
TIFFClose(Source);
end;
Dans l'ordre, nous avons ouverture du fichier, récupération d'informations qui sont stockées dans les tags de l'image.
Puis lecture du contenu de l'image de façon similaire au scanline d'un bitmap.
On se retrouve presque à lire une image Tiff comme une image Bitmap.
Je vous passe les détails sur la détermination de la palette. Vous irez voir dans le code source.
Pour l'écriture d'une image Tiff :
Destination:=TIFFOpen(DestFileName,'w');
assert(Destination<>nil,'Can''t create Destination file');
try
assert(TIFFSetField(Destination,TIFFTAG_IMAGEWIDTH,Width)=1,'Problem with TIFFTAG_IMAGEWIDTH');
assert(TIFFSetField(Destination,TIFFTAG_IMAGELENGTH,Height)=1,'Problem with TIFFTAG_IMAGELENGTH');
assert(TIFFSetField(Destination,TIFFTAG_BITSPERSAMPLE,8)=1,'Problem with TIFFTAG_BITSPERSAMPLE');
assert(TIFFSetField(Destination,TIFFTAG_SAMPLESPERPIXEL,1)=1,'Problem with TIFFTAG_SAMPLESPERPIXEL');
assert(TIFFSetField(Destination,TIFFTAG_ROWSPERSTRIP,Height)=1,'Problem with TIFFTAG_ROWSPERSTRIP');
assert(TIFFSetField(Destination,TIFFTAG_COMPRESSION,COMPRESSION_NONE)=1,
'Problem with TIFFTAG_COMPRESSION');
assert(TIFFSetField(Destination,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_PALETTE)=1,
'Problem with TIFFTAG_PHOTOMETRIC');
assert(TIFFSetField(Destination,TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)=1,
'Problem with TIFFTAG_PLANARCONFIG');
SetLength(Red_Tbl,256);
SetLength(Green_Tbl,256);
SetLength(Blue_Tbl,256);
for I:=0 to PaletteBuilder.NbColor-1 do
begin
MyColor:=PaletteBuilder.Color[I];
Red_Tbl[I]:=MyColor.X*256;
Green_Tbl[I]:=MyColor.Y*256;
Blue_Tbl[I]:=MyColor.Z*256;
end;
assert(TIFFSetField(Destination,TIFFTAG_COLORMAP, @(red_tbl[0]), @(green_tbl[0]),
@(blue_tbl[0]))=1,'Problem setting the ColorMap');
PaletteBuilder.TableToPalette(Density);
setLength(DestLine,Width);
for J:=0 to Height-1 do
begin
TIFFReadScanline(Source,@(SourceLine[0]),J,0);
for I:=0 to Width-1 do
DestLine[I]:=Density[SourceLine[3*I],SourceLine[3*I+1],SourceLine[3*I+2]];
TIFFWriteScanline(Destination,@(DestLine[0]),J,0);
end;
finally
TiffClose(Destination);
end;
Création de l'objet toujours sans problème. Là, il y a un peu plus de tags à fournir.
Il faut enregistrer la palette dans le fichier Tiff.
Et finir en enregistrant l'image elle-même toujours avec un scanline.
IV. Conclusion
Le programme utilisé ici comme exemple me permet de réduire en 256 couleurs des vues satellite que
j'ai générées avec un autre logiciel personnel.
Le temps de calcul sur une image de 18000x16000 (800Mo) est de 13 minutes quand Photoshop a besoin d'une demi-heure et
que Gimp n'arrive même pas à ouvrir l'image.
Le résultat, en terme de qualité d'image, est tout à fait acceptable.
Pour tenir compte de la sensibilité de l'oeil, il serait sans doute plus judicieux de travailler dans les espaces
YCrCb ou TSV (au lieu de RVB) et de compter double les longueurs sur l'axe de la luminosité (Y ou T suivant le modèle).
Sur l'utilisation du Tiff, la traduction pour Delphi de LibTiff permet une gestion aisée des images Tiff.
On peut aussi accéder à tous les tags contenus dans les images Tiff.
Je suis par exemple en train de travailler à la récupération des tags GeoTiff pour l'étalonnage des vues spatiales et aériennes.
|