Projet FOGRIMMI Image Management Documentation

Transcription

Projet FOGRIMMI Image Management Documentation
Image management
07/01/08 page 1/23
Projet FOGRIMMI
Image Management
Documentation
Version 1.0
Auteur:
Thibaut Aubrun - Ingénieur de recherche
Coordinateur Scientifique:
Olivier Lezoray – Université de Caen Basse Normandie
Projet réalisé dans les locaux de l'IUT Cherbourg Manche
Image management
07/01/08 page 2/23
Table des matières
I.Programmer avec TIFF Image Management.........................................................3
A.Travailler sur un Tiff uni-page ...............................................................................................3
B.Travailler sur un Tiff multi-page............................................................................................9
II.Développer la bibliothèque.................................................................................10
A. Architecture:........................................................................................................................10
B.Documentation:....................................................................................................................11
C.Utilisation des outils de debuggage:.....................................................................................11
D.Format de fichiers:...............................................................................................................13
E.Compilation:.........................................................................................................................15
F.Différences Libtiff et Bigtiff.................................................................................................16
III.Améliorations futures........................................................................................17
A.Les améliorations à apporter:...............................................................................................17
B.Les corrections:....................................................................................................................17
But du document:
Image management
07/01/08 page 3/23
Ce document présente dans un premier temps pour le développeur les fonctionnalités de
base de l'API TIFF Image Management. Dans un second temps, il présente les éléments essentiels
pour continuer le développement de cette API ainsi que l'outil de visualisation.
I. Programmer avec TIFF Image Management
A.Travailler sur un Tiff uni-page
■
Lecture d'une image quelconque
L'ouverture d'une image Tiff permet de récupérer les informations la concernant. Une structure de
l'API permet de stocker ces informations (Tiff_Properties). La création d'un IM_Tiff, comme
démontré ci dessous, ouvre l'image, rempli les propriétés de la struture mais ne lit pas le contenu.
Pour cela il faut définir un cadre de lecture ou lire un pixel. Voir les exemples qui suivent.
Prenons un exemple simple de lecture et de récupération des informations:
try{
// Creation d'un IM_Tiff
// Les informations des pages sont alors chargées en mémoire
IM_Tiff* tif = new IM_Tiff(« fileName.tif »);
// Proprietes du tiff
TIFF_Properties tiffP = tif->getProperties();
// Coordonnées du cadre de lecture
IM_Box box;
// Coordonnées du coin haut gauche
box.XTop = 1000;
box.YTop = 1000;
// Coordonnées du coin bas droit exclu
box.XBtm = 2000;
box.YBtm = 2000;
// Lecture de la page
tif->read(box);
delete tif;
}
catch(...)
{
throw;
}
Vous trouverez dans la documentation HTML (générable via doxygen) le descriptif de la structure
Tiff_Properties ainsi que les différents tags stockés.
Une image Tiff peut être organisée en tuiles ou en lignes. Ce sont des composants différents mais
Image management
07/01/08 page 4/23
ayant des propriétés communes: une largeur, une hauteur et un buffer. La lecture d'une partie d'une
image tiff entraîne le chargement de plusieurs composants.
Voici un schéma montrant une image Tiff tuilée dont les tuiles chargées(en rouge) correspondent au
cadre de lecture(bleu).
0,0
0,1
1,0
1,1
0,2
0,3
0,5
3,5
■
Créer de nouvelles pages par interpolation
Dans ce cas, nous souhaitons créer à partir d'une image de base un Tiff multipage en
diminuant la résolution entre chaque nouvelle page (i.e. une pyramide régulière).
Cette fonctionnalité est offerte par la classe IM_Tools:
try
{
IM_Tools *tool = new IM_Tools();
tool->constructMultiPage(fileName, "../resultat/multipage.tif", 3, 0.5, NN);
}
catch(...)
{
throw;
}
Dans cet exemple, nous avons créé un Tiff multipage en ajoutant 3 pages au fichier « fileName »,
diminuées d'un facteur de 0.5 par une interpolation linéaire (NN).
Les différentes interpolations implémentées sont:
● NN : Nearest Neighbour
● BL : Bi-Linéaire
● BC : Bi-Cubique
■
Lire et sauvegarder une partie de l'image
Pour sauvegarder une partie d'une image Tiff, il y a des étapes à respecter. Nous allons
charger un fichier Tiff, lire une partie de l'image selon un cadre de lecture et la sauvegarder dans un
nouveau fichier.
Image management
07/01/08 page 5/23
// Gestion des exceptions
// Si une exception d'exécution est levée dans IMTiff alors elle sera propagée
try{
// Creation d'un IM_Tiff
// Les informations des pages sont alors chargées en mémoire
IM_Tiff* tif = new IM_Tiff(« fileName.tif »);
// Coordonnées du cadre de lecture
IM_Box box;
// Coordonnées du coin haut gauche
box.XTop = 1000;
box.YTop = 1000;
// Coordonnées du coin bas droit
box.XBtm = 2000;
box.YBtm = 2000;
// Lecture de la page
tif->read(box);
// Ecriture du cadre de lecture de la page courante dans le fichier name
tif->write(« name.tif »);
delete tif;
}
catch(...)
{
throw;
}
Lorsque nous appelons la fonction « write », la bibliothèque considère que nous souhaitons écrire
un nouveau Tiff selon le dernier cadre de lecture défini avec les mêmes propriétés de compression,
de taille de tuile, ... .
Pour changer la compression utilisée, par exemple une image en LZW ré-encodé en Jpeg, on
implémentera ceci:
Image management
07/01/08 page 6/23
// Definition des nouvelles proprietes du tiff
TIFF_Properties tiffP;
tiffP.name= « name.tif »;
tiffP.compression = COMPRESSION_JPEG;
tiffP.bps = 8;
tiffP.componentHeight = 240;
tiffP.componentWidth = 240;
tiffP.width = (unsigned int)(box.XBtm - box.XTop);
tiffP.height = (unsigned int)(box.YBtm - box.YTop);
tiffP.jpeg_cm = 0;
tiffP.jpeg_q = 75;
tiffP.photomet = PHOTOMETRIC_RGB;
tiffP.planar = 1;
tiffP.pixOrganisation = TILE;
tiffP.colorMode = 3;
tiffP.xRes
= 0;
tiffP.yRes
= 0;
tiffP.resUnit
= RESUNIT_INCH;
// Ecriture avec conversion du tiff
tif->write(tiffP);
■
Lire les valeurs RGB d'un pixel
La lecture d'un pixel peut se faire de deux manières différentes. La première, par l'opérateur
parenthèse (page, row, col), permet de récupérer la structure IM_Pixel de coordonnées définies sur
la page Tiff lue.
Image management
07/01/08 page 7/23
try
{
IM_Tiff* tif = new IM_Tiff(« fileName.tif »);
// Valeurs du pixel X=1, Y= 2 de la première page (n° 0)
IM_Pixel pix = tif(1,2,0);
//Accès aux valeurs
std::cout << "PIX: X="<<pix.x<<" Y="<<pix.y<<"\n";
std::cout << "VALUE: R="<<pix.value[0]<<" G="<<pix.value[1]<<" B="<<pix.value[2]<<"\n";
delete tif;
}
catch(std::exception &e)
{
throw;
}
La deuxième manière est de passer en paramètres la structure IM_Pixel afin d'y affecter les valeurs.
try
{
IM_Tiff* tif = new IM_Tiff(« fileName.tif »);
IM_Pixel pix;
pix.x = 1;
pix.y = 2;
tif->getPixelValue(pix);
//Acces aux valeurs
std::cout << "PIX: X="<<pix.x<<" Y="<<pix.y<<"\n";
std::cout << "VALUE: R="<<pix.value[0]<<" G="<<pix.value[1]<<" B="<<pix.value[2]<<"\n";
delete tif;
}
catch(std::exception &e)
{
throw;
}
Dans ces deux exemples nous avons omis de préparer le Tiff en lisant la tuile contenant le pixel. La
bibliothèque va alors s'en charger. Sinon, un exemple est fourni à la page suivante avec le
chargement d'une tuile au préalable.
Voici un exemple complet de lecture des pixels d'une image:
Image management
07/01/08 page 8/23
try
{
IM_Tiff* tif = new IM_Tiff(« fileName.tif »);
// Valeurs d'un pixel de la première page
IM_Pixel pix;
for(unsigned int row = 0; row < tif->getPageHeight(); row++)
{
for(unsigned int col = 0; col < tif->getPageWidth(); col++)
{
pix = tif(col, row, 0);
//Accès aux valeurs
std::cout << "PIX: X="<<pix.x<<" Y="<<pix.y<<"\n";
std::cout << "VALUE: R="<<pix.value[0]<<" G="<<pix.value[1]<<" B="<<pix.value[2]<<"\n";
}
}
delete tif;
}
catch(std::exception &e)
{
throw;
}
Pour lire seulement une tuile dans une image Tiff on peut le réaliser de cette manière:
Image management
07/01/08 page 9/23
try
{
IM_Tiff* tif = new IM_Tiff(« fileName.tif »);
// Valeurs d'un pixel
IM_Pixel pix;
// On recupère les propriétés du Tiff
Tiff_Properties tiffP = tif->getProperties();
//Définition du cadre de lecture, données à lire
//Lire la tuile (ou ligne) de coordonnées row = 2, col = 4
IM_Box box;
box.XTop = 4*tiffP.componentWidth;
box.YTop = 2*tiffP.componentHeight;
box.XBtm = 5*tiffP.componentWidth;
box.YBtm = 3*tiffP.componentHeight;
tif->read(box);
for(unsigned int row = 2*tiffP.componentHeight; row < 3*tiffP.componentHeight; row++)
{
for(unsigned int col = 4*tiffP.componentWidth; col < 5*tiffP.componentWidth; col++)
{
pix = tif(col, row, 0);
//Accès aux valeurs
std::cout << "PIX: X="<<pix.x<<" Y="<<pix.y<<"\n";
std::cout << "VALUE: R="<<pix.value[0]<<" G="<<pix.value[1]<<" B="<<pix.value[2]<<"\n";
}
}
delete tif;
}
catch(std::exception &e)
{
throw;
}
Pour avoir un accès plus rapide, lorsque l'on souhaite accéder à plusieurs tuiles l'une après l'autre,
getData() permet de récupérer le buffer d'une tuile. En contre partie, cela demande un peu plus
d'effort de la part du programmeur.
Image management
07/01/08 page 10/23
try
{
IM_Tiff* tif = new IM_Tiff(« fileName.tif »);
// Valeurs du pixel
IM_Pixel pix;
// On recupère les propriétés du Tiff
Tiff_Properties tiffP = tif->getProperties();
//Lire la tuile(ou ligne) de coordonnées row = 2, col = 4
IM_Box box;
box.XTop = 4*tiffP.componentWidth;
box.YTop = 2*tiffP.componentHeight;
box.XBtm = 5*tiffP.componentWidth;
box.YBtm = 3*tiffP.componentHeight;
tif->read(box);
uint8* allData;
if((allData=(uint8*)malloc(tiffP.componentWidth*tiffP.componentHeight*tiffP.colorMode*sizeof(uint8))) ==
NULL) {
std::cout <<« Malloc error\n »;
break;
}
// Ou bien on peut utiliser une méthode la libtiff
//if((allData=(uint8*)malloc(TIFFTileSize(&(tiffP.tif) ))) == NULL) {
// std::cout <<« Malloc error\n »;
// break;
// }
// ici on peut boucler sur plusieurs tuiles par exemple.
allData = tif->getData(0, 2, 4);
unsigned int n = 0;
for(unsigned int row = 0; row < tiffP.componentHeight; row++)
{
for(unsigned int col = 0; col < tiffP.componentWidth; col++)
{
//Acces aux valeurs
std::cout << "VALUE: R="<<allData[n]
n++;
std::cout<<" G="<<allData[n];
n++
std::cout<<" B="<<allData[n]<<"\n";
n++;
}
}
delete tif;
}
catch(std::exception &e)
{
throw;
}
Image management
07/01/08 page 11/23
B.Travailler sur un Tiff multi-page
Les traitements ne sont guère différents. Pour chaque méthode de la classe IM_Tiff il est
possible de rajouter le numéro de page sur lequel nous travaillons.
Des opérations supplémentaires sont disponibles sur un Tiff multi-pages.
■
Ajouter ou supprimer une page
Après avoir créé un Tiff multi-pages nous pouvons être amenés à ajouter, supprimer ou extraire une
page.
Ajout:
try
{
IM_Tools *tool = new IM_Tools();
// ajoute une page en fin
tool->append(« fileName.tif », « fileAdd.tif »);
delete tool;
}
catch(...)
{
throw;
}
Suppression:
Image management
07/01/08 page 12/23
Image management
07/01/08 page 13/23
try
{
IM_Tiff* tif = new IM_Tiff(fileName);
int index = 0; // supprime la première page
tif->remove(index);
delete tif;
}
catch(...)
{
throw;
}
Splitter les pages:
try
{
IM_Tools* tool = new IM_Tools();
//Extrait les pages en les prefixant de « prefix »
tool->split(« fileName.tif », "prefix");
// Elles sont sauvegardées automatiquement
delete tool;
}
catch(...)
{
throw;
}
II.Développer la bibliothèque
Image management
07/01/08 page 14/23
Image management
07/01/08 page 15/23
A. Architecture:
Illustration 1: Diagramme de classe
Une image IM_Tiff est une IM_Image qui est composée de plusieurs IM_Page. Les données des
pages sont contenues dans des IM_Component organisés soit en IM_Tile soit en IM_Strip.
La classe IM_Tools met à la disposition du développeur quelques routines sur un IM_Tiff.
Image management
07/01/08 page 16/23
Exemple: créer un Tiff multi-page à partir d'une image de base.
La classe IM_ImageMemory permet de convertir les données contenues dans les IM_Component en
un tableau unidimensionnel. Ceci est utile pour effectuer des interpolations avec la classe IM_Zoom
qui prends une IM_ImageMemory source et retourne une IM_ImageMemory interpolée.
B.Documentation:
La bibliothèque Tiff Image Management dispose d'une documentation sous forme HTML
générable par doxygen. Elle est disponible dans le répertoire « IM_Tiff/Documentation/html ».
La génération de cette documentation peut se faire via l'interface GUI doxywizard couplé au fichier
Doxyfile présent dans le répertoire parent. Une génération automatique est aussi possible lors de la
compilation avec Cmake (Se reporter au chapitre correspondant).
C.Utilisation des outils de debuggage:
●
Utilisation du logger
Afin de faciliter l'activation ou non des traces d'exécution, un Logger est implémenté. Nous
pouvons, au choix, afficher les traces dans une console ou bien sauvegarder dans un fichier. Ce
choix est fait au départ en instanciant la classe CloggerDebug ou CloggerFile comme suit:
#include « Core/IM_Tiff.h »
ILogger::SetLogger(new CloggerDebug());
ou
ILogger::SetLogger(new CLoggerFile(« ouput.txt »));
Pour écrire, il suffit d'employer la méthode suivante:
CFile fileName = « fichier.tif »;
ILogger::Log("Ouverture : %s\n", fileName.Fullname().c_str());
ILogger::Log() <<"Ouverture "<<fileName.Filename()<< "\n";
Deux styles d'écritures sont disponibles, façon C ou C++. La classe CFile permet de faire quelques
manipulation sur les fichiers. Voir documentation.
Pour obtenir la trace complète en mode fichier nous devons détruire l'instance du logger.
//Detruit le logger - obligatoire en mode fichier
Ilogger::SetLogger(NULL);
Pour permettre d'activer ou de désactiver le logger il suffit de définir une variable du préprocesseur
« _DEBUG_ ». On utilise comme suit:
Image management
07/01/08 page 17/23
#ifdef _DEBUG_
Ilogger::Log()<< « Creation d'un Tiff\n »;
#endif
●
Utilisation du traqueur de fuite mémoire
Un autre outil très utile pour le développeur est le traqueur de fuite mémoire. Il est possible
de l'activer ou non très facilement. Si _DEBUG_ est défini dans les variables préprocesseur alors il
est activé.
Pour s'en convaincre il suffit d'ouvrir le fichier
« IM_Tiff/Src/Debug/IM_DebugNew.h ». Les
opérateurs d'affections ne sont surchargés que lorsque cette variable est définie.
Pour traquer les fuites mémoires dans le code, il faut inclure dans le fichier « .cpp » voulant être
analysé l'inclusion « #include "Debug/IM_DebugNew.h" » après toutes autres inclusion de la
bibliothèque.
Si vous êtes amené à inclure ce fichier dans un header il faut absolument inclure en fin de fichier
« #include "Debug/IM_DebugNewOff.h" » afin de ne pas perturber le comportement des autres
headers inclus par la suite.
#include <math.h>
#include « Fichier.h »
// on inclus en dernier les surcharges d'opérateur
#include « Debug/IM_DebugNew.h »
class MaClasse
{
MaClasse()
{
// On fait une affectation que l'on souhaite traquer
Fichier fich = new Fichier(« toto.txt »);
};
};
// On supprime les surcharge d'opérateur
#include « Debug/IM_DebugNewOff.h »
●
Utilisation des exceptions
Plusieurs types d'exceptions sont définies dans Tiff Image Management. Ceci permet de
mieux traquer les erreurs et de relancer si nécessaire les calculs. Voici les différentes exceptions
implémentées:
Image management
07/01/08 page 18/23
Illustration 2: Exceptions
A noter qu'une exception levée générera un fichier de log avec le type d'exceptions ainsi que le
message associé.
D.Format de fichiers:
●
L'API définie des ensembles de lignels. Un lignel est composé de deux coordonnées de
pixels adjacents. Un ensemble de lignels se suivant dans le sens anti-horaire définie un
contour. Les lignels peuvent être exportés sous deux formats de fichier:
○
Fichier texte:
Image management
07/01/08 page 19/23
[GENERAL]
imgName=../../../../test2.svs
[LIGNELS]
[GROUP=0]
[GROUP=1]
[INDEX=0]
x1:103,y1:100,x2:104,y2:100
x1:103,y1:101,x2:104,y2:101
x1:103,y1:101,x2:103,y2:102
x1:102,y1:101,x2:102,y2:102
....
x1:105,y1:99,x2:105,y2:100
x1:104,y1:99,x2:104,y2:100
x1:103,y1:100,x2:104,y2:100
[GROUP=2]
[INDEX=0]
x1:5000,y1:5000,x2:5001,y2:5000
x1:5000,y1:5001,x2:5001,y2:5001
...
x1:5000,y1:5000,x2:5001,y2:5000
[INDEX=1]
x1:5003,y1:5000,x2:5004,y2:5000
x1:5003,y1:5001,x2:5004,y2:5001
....
x1:5004,y1:4999,x2:5004,y2:5000
x1:5003,y1:5000,x2:5004,y2:5000
[END]
○
Fichier binaire:
Les sections sont remplacées par un entier négatif selon la notation suivante:
group = -1
index = -2
coord = -3
fin = -4
La correspondance entre le fichier texte et binaire se fait comme ceci:
[GROUP=0]
[GROUP=1]
[INDEX=0]
x1:103,y1:100,x2:104,y2:100
x1:103,y1:101,x2:104,y2:101
x1:103,y1:101,x2:103,y2:102
x1:102,y1:101,x2:102,y2:102
[GROUP=2]
-1-1-2-3103100104100-31031011041013103101103102-3102101102102-1
Note : lors de l'écriture du fichier binaire, on insère en première position un entier négatif égal à -5.
Cela provoque un bug à la lecture sinon.
●
Recomposition de fichiers composites en une image Tiff
Image management
07/01/08 page 20/23
Une image de très grande taille peut être sauvegardée en plusieurs petits fichiers. Pour
retrouver l'ordre de recomposition des ces fichiers composites on définit un fichier XML.
Voici un exemple simple de fichier XML:
<?xml version="1.0"?>
<root>
<properties name="../resultat/composite2.tif" width="2750.000000" height="2371.500000" tileWidth="240"
tileHeight="240" bps="8" colorMode="3" photomet="2" planar="1" >
<compression type="7" quality="75" colorMap="0" />
</properties>
<positions>
<position X="0" Y="0" name="composite2/composite00.tif" />
<position X="0" Y="1" name="composite2/composite01.tif" />
</positions>
</root>
Exemple d'utilisation: On procède à une décomposition puis recomposition dans le cas d'un zoom
sur une grande image. On découpe en morceaux, on applique une interpolation sur chaque
morceaux puis on recompose l'image finale.
E.Compilation:
Pour compiler les sources de la bibliothèque, du programme de démonstration et du viewer
plusieurs manières de procéder sont disponibles. Avant tout il faut installer dans cette ordre les
librairies suivantes:
libtiff
libjpeg
libz
gettext
pkg-config
glib
libsigc++
glibmm
libxml-2
libxml++
cmake
Sous windows il faut également installer le compilateur de visual studio 2005, le plateforme SDK.
●
Utiliser CMake
CMake est un outil permettant de compiler les sources en s'adaptant à la plate-forme de
Image management
07/01/08 page 21/23
développement. Le fichier CmakeList.txt présent dans le dossier racine de la bibliothèque permet de
configurer le makefile et de compiler les sources. Cela permet également de générer la documention
HTML via doxygen.
Taper dans une console sous le dossier contenant CMakeList.txt:
Configuration et documentation :
Sous unix
cmake .
Sous windows
cmake . -G « NMake Makefiles »
Si quelques erreurs se produisent ne pas hésiter à effacer le cache créé.
Compilation :
Unix : make
Windows : nmake
Le compilateur utilisé doit être GCC sous système Unix. Sous windows le compilateur VS2005 est
employé. Cependant, un patch doit pour l'instant être employé pour compiler le viewer utilisant
QT4. Il semblerait à l'heure où ces lignes sont écrites, que Trolltech ait rendu possible la
compilation sans patch.
●
Code::blocks
Sous système unix (Linux et Mac) l'utilisation de l'IDE Code::Blocks est possible. Un
workspace est présent dan le dossier CBWorkSpace. Il vous faudra peut être modifier quelques
paths. Le compilateur utilisé est GCC.
Sous windows il faut revoir les paths entièrement. A noter que la compilation avec Mingw-gcc
échouera avec la librairie BigTiff. Il faut donc employer le compilateur VS2005 avec un patch pour
le viewer.
●
Patch VS2005
Voici un tutorial expliquant la démarche à suivre pour compiler QT4 avec le compilateur de
VS2005:
http://arb.developpez.com/qt4/vc++/compilation/
En fin d'explication QT4.1.1 patché est disponible en téléchargement.
F. Différences Libtiff et Bigtiff
Extérieurement, aucunes différences dans le code n'est visible. De nouveaux type sont
apparus pour supporter l'encodage en 64 bits.
Pour plus d'information sur les changements bas-niveaux:
Image management
07/01/08 page 22/23
http://www.remotesensing.org/libtiff/v4.0.0.html
III.Améliorations futures
Plusieurs améliorations ou corrections ne sont pas faites à ce jour.
A.Les améliorations à apporter:
●
●
●
●
●
●
●
●
Conversion Tuile vers Ligne
Lignel : sauvegarde en XML, import
Profileur de code
La fonction cloneFromLignel permet de réarranger dans l'espace les contours définie par des
ensembles de lignels dans une nouvelle image. Il faut alors sauvegarder la correspondance
position initiale des contours avec leurs nouvelles positions dans la nouvelle image.
Supporter plus de tags, plus de conversions
Sauvegarde d'un ensemble de lignels en particulier
Paramétrer la taille maximum du cadre de lecture en fonction de la RAM disponible
Lors de la récupération de toutes les données d'un cadre de lecture, augmenter la vitesse de
traitement en faisant un « memcpy », voir la fonction getAllData() dans IM_Page.cpp.
B.Les corrections:
●
●
●
●
●
Ajouter des attributs protected: hauteur largeur component et colorMode
Pour les IM_Tile, la méthode addData() utilise une hauteur et une largeur fixe. Remplacer
par une copie de tableau (memcpy).
tiff is valid – ajouter plus de contrôle.
Modifier les valeurs des pixels et pouvoir ré-écrire dans le même fichier
Lors de l'ajout de pages dans un bigtiff, si l'on dépasse 4Go alors le nombre de pages
Image management
●
07/01/08 page 23/23
redevient 1 alors que la taille du fichier augmente toujours. Si l'on rajoute une nouvelle page
nous obtenons bien 2 pages.
Méthode remove de suppression d'une page i supprime les pages j<i.