User Tools

Site Tools


cpp:max-tree

This is an old revision of the document!


Exemple Max-Tree

Il est possible d'écrire un programme dédié à la construction d'un arbre en utilisant la bibliothèque Triskele. 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 <boost/algorithm/string.hpp>
 
// 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<typename PixelT> void filter (const IImage<GeoDimT> &inputImage, IImage<GeoDimT> &outputImage, const vector <DimImgPack> &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 <string> values;
  split (values, argv[3], is_any_of (","));
  vector <DimImgPack> 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<GeoDimT> inputImage (argv[1]);
  // get metadata
  inputImage.readImage ();
  // get image size (width x height)
  const Size<GeoDimT> size (inputImage.getSize ());
 
  IImage<GeoDimT> 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<uint8_t> (inputImage, outputImage, thresholds); break;
  case GDT_UInt16:
    filter<uint16_t> (inputImage, outputImage, thresholds); break;
  case GDT_Int16:
    filter<int16_t> (inputImage, outputImage, thresholds); break;
  case GDT_UInt32:
    filter<uint32_t> (inputImage, outputImage, thresholds); break;
  case GDT_Int32:
    filter<int32_t> (inputImage, outputImage, thresholds); break;
  case GDT_Float32:
    filter<float> (inputImage, outputImage, thresholds); break;
  case GDT_Float64:
    filter<double> (inputImage, outputImage, thresholds); break;
 
  default :
    cerr << "unknown type!" << endl; break;
    return 1;
  }
  return 0;
}

Voici la fonction principale du programme.

template<typename InPixelT>
void
filter (const IImage<GeoDimT> &inputImage, IImage<GeoDimT> &outputImage, const vector <DimImgPack> &thresholds) {
 
  // switch on debug mode
  // Log::debug = true;

Nous lisons les pixels de l'image.

  // empty raster
  Raster<InPixelT, GeoDimT> raster;
  // true image size (width x height)
  const Size<GeoDimT> 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<GeoDimT> border (size, false);
  // neighbors take in account (default connectivity C4)
  const GraphWalker<GeoDimT> graphWalker (border);
  // tree builder base on raster, connectivity and type of tree
  ArrayTreeBuilder<PixelT, PixelT, GeoDimT> 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<GeoDimT> tree (coreCount);
  // reserve nodes weight attributes
  WeightAttributes<PixelT, GeoDimT> 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<GeoDimT> 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<PixelT, GeoDimT> 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 <vector <OutPixelT> > allBands (outputChannelCount, vector<OutPixelT> (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
cpp/max-tree.1656051120.txt.gz · Last modified: 2022/06/24 06:12 by francois