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.