====== Exemple Max-Tree ======
Il est possible d'écrire un programme dédié à la construction d'un arbre en utilisant la bibliothèque Triskele.
Voici un exemple tiré des sources {{ :cpp:testmaxtree.cpp |}}
Ce programme prend 3 arguments
* le nom du fichier contenant l'image en entrée qui construira un arbre MAX
* le nom du fichier qui contiendra l'image produite
* une liste de valeurs (séparée par des virgules) pour réaliser les élagages
Il faut commencer par les déclarations d'usage.
La première inclusion sera utilisée pour convertir la liste de paramètre en valeur de seuil pour élagueur l'arbre.
Les autres ne sont pas indispensables, car elles seront dans tous les cas incluses par Triskele.
// is_any_of
#include
// pixels type
#include "gdal.h"
// log functions
#include "obelixDebug.hpp"
// Point and Size (2D or 3D)
#include "obelixGeo.hpp"
// read/write images
#include "IImage.hpp"
// nodata pixels definition in images
#include "Border.hpp"
De même, on peut déclarer les classes Triskele qui pourront être utilisées.
#include "ArrayTree/triskeleArrayTreeBase.hpp"
#include "ArrayTree/ArrayTreeBuilder.hpp"
#include "Attributes/WeightAttributes.hpp"
#include "Attributes/AreaAttributes.hpp"
#include "AttributeProfiles.hpp"
En fonction de ce qui a été déclaré, il peut être utile de simplifier les appels en omettant le contexte d'utilisation (par exemple en évitant de préfixer avec ''std::'').
using namespace std;
using namespace boost;
using namespace obelix;
using namespace obelix::triskele;
Dans notre exemple, nous nous limiterons à des images 2D et la construction d'un arbre MAX.
static const int GeoDimT (2);
static const TreeType treeType = MAX;
Nous déclarons la fonction ''filtre'' qui réalisera l'élagage et l'écriture du résultat. Elle sera donc dépendante de la nature des pixels lus ''InPixelT'', de l'image en entrée et de la liste des seuils de coupure.
template void filter (const IImage &inputImage, IImage &outputImage, const vector &thresholds);
Le point d'entré du programme, commencera par analyser les arguments
* argv[0] : nom du programme
* argv[1] : nom du fichier contenant l'image en entrée
* argv[2] : nom du fichier contenant l'image produite
* argv[3] : liste des seuils séparé par des virgules
int
main (int argc, char** argv, char** envp) {
// get parameters
if (argc != 4) {
cout << "Usage: " << argv[0] << " input-filename outpout-filename thresholds,..." << endl;
exit (1);
}
// set thresholds vetcor
vector values;
split (values, argv[3], is_any_of (","));
vector thresholds (values.size ());
transform (values.begin (), values.end (), thresholds.begin (), [] (const string &val) { return stoi (val); });
Ensuite nous lisons les métadonnées de l'image pour déterminer la nature des pixels et créer le fichier qui contiendra l'image produite.
IImage inputImage (argv[1]);
// get metadata
inputImage.readImage ();
// get image size (width x height)
const Size size (inputImage.getSize ());
IImage outputImage (argv[2]);
// set metadata
outputImage.createImage (size, inputImage.getDataType (), thresholds.size (),
inputImage, NullPoint2D);
Enfin nous réalisons le traitement en invoquant la fonction ''filtre'' avec le bon type.
switch (inputImage.getDataType ()) {
case GDT_Byte:
filter (inputImage, outputImage, thresholds); break;
case GDT_UInt16:
filter (inputImage, outputImage, thresholds); break;
case GDT_Int16:
filter (inputImage, outputImage, thresholds); break;
case GDT_UInt32:
filter (inputImage, outputImage, thresholds); break;
case GDT_Int32:
filter (inputImage, outputImage, thresholds); break;
case GDT_Float32:
filter (inputImage, outputImage, thresholds); break;
case GDT_Float64:
filter (inputImage, outputImage, thresholds); break;
default :
cerr << "unknown type!" << endl; break;
return 1;
}
return 0;
}
Voici la fonction principale du programme.
template
void
filter (const IImage &inputImage, IImage &outputImage, const vector &thresholds) {
// switch on debug mode
// Log::debug = true;
Nous lisons les pixels de l'image.
// empty raster
Raster raster;
// true image size (width x height)
const Size size (inputImage.getSize ());
// read first band (0) in 2D mode from origine [0,0] to end [width, height].
inputImage.readBand (raster, 0, NullPoint2D, size);
Nous déclarons l'objet arbre qui contiendra le chaînage des feuilles vers la racine.
// no border (i.e. all pixels are take in account)
const Border border (size, false);
// neighbors take in account (default connectivity C4)
const GraphWalker graphWalker (border);
// tree builder base on raster, connectivity and type of tree
ArrayTreeBuilder atb (raster, graphWalker, treeType);
// get the number of core
const DimCore coreCount (boost::thread::hardware_concurrency ());
// declare empty tree and set the number of thread for all algorithms
Tree tree (coreCount);
// reserve nodes weight attributes
WeightAttributes weightAttributes (tree, getDecrFromTreetype (treeType));
Nous déclenchons la construction de l'arbre.
* ATB contient les éléments de l'images (matrice de pixels, connectivité, type d'arbre)
* tree coquille vide qui va contenir l'arbre
* weightAttributes est le premier tableau d'attributs associés aux nœuds (niveaux de gris)
atb.buildTree (tree, weightAttributes);
Nous créons un second tableau d'attributs associés aux nœuds (taille en pixels).
// create area attribute
const AreaAttributes areaAttributes (tree);
récupération des cardinalités.
// output channels count
const DimChannel outputChannelCount (thresholds.size ());
// number of pixels (width x height)
const DimImg pixelsCount (size.getPixelsCount ());
Nous produisons des bandes de sorti (élagage de l'arbre)
// write node grayscale as attribute profiles
AttributeProfiles attributeProfiles (tree);
// copy weightAttributes to attributeProfiles
atb.setAttributProfiles (attributeProfiles);
Nous réalisons toutes les coupures pour tous les seuils définis en arguments du programme.
// result allocation memory
vector > allBands (outputChannelCount, vector (pixelsCount, 0));
// do all cuts
areaAttributes.cut (allBands, attributeProfiles, thresholds);
Nous recopions chaque matrice de pixels dans dans le fichier de sorti.
// for all cuts
for (DimChannel channel (0); channel < outputChannelCount; ++channel)
// write output raster
outputImage.writeBand (allBands[channel].data (), channel);
}
===== Utilisation =====
Si l'on fait l'hypthèse d'être dans le répertoire ''build'' avec la production d'une bibliothèse ''libtriskeledebug.so'' a partir de l'option ''Debug'' de ''cmake'', voici a quoi peut ressembler la commende de compilation :
g++ -DNO_OTB -I../triskele/include -I/usr/include/hdf5/serial -I/usr/include/gdal -pthread -g -DENABLE_LOG -std=gnu++11 ../triskele/test/TestMaxTree.cpp -o testMaxTree -L . -ltriskeledebug -lhdf5_serial -lhdf5_serial_cpp -lstdc++ -lpthread -lboost_system -lboost_chrono -lboost_thread -lboost_program_options -lboost_date_time -lboost_serialization -lboost_filesystem -lboost_unit_test_framework -lgdal -ltbb -ltinyxml
Sinon, il est possible d'enrichir le fichier ''CMakeLists.txt'' de la manière suivante :
message ("sample max tree")
add_executable (testMaxTree "test/TestMaxTree.cpp")
target_compile_definitions (testMaxTree PUBLIC -DNO_OTB)
target_link_libraries (testMaxTree ${allLibs})
Voici un exemple d'utilisation
./testMaxTree strasbourg.tif output.tif 10,100,10000