Prima Homepage
ImaLab
Generating C++ Modules
User Manual
The Command Shell
Running Imalab
Plugin Process
Pixels and Images
Interactive selection
Graphics: plots, profiles
Image file I/O
Image display
Connectivity Analysis
Image Processing(1)
Gaussian operators
Technical Documentation
Creating New Modules
Tutorial
Tutorial Download

[PREV][SUIV]

Connectivity Analysis

Le module modaaregion propose un ensemble de classes permettant une segmentation d'images sophistiquée et rapide, selon des critères programmables; et une analyse en régions performante.

La segmentation procède en deux étapes: étiquettage des pixels, analyse de connexité; qui peuvent être suivies d'une troisième étape: calcul de propriétés supplémentaires des régions.

Exemple d'enchainement d'opérations.
Prenons un cas simple: on veut segmenter une image de flottants iflt1 selon le signe des pixels.


etiq = new RLEImage(iflt1,0);
affiche-rle(etiq,1,255,0); // affichage facultatif
reglist = a-regions(etiq,10);

Ceci crée d'abord l'image d'étiquettes etiq, à partir de l'image de flottants iflt1; l'argument 0 indique le critère d'étiquettage. La fonction d'analyse de regions a-regions prend comme arguments l'image d'étiquettes et un seuil sur la taille des régions, et fournit comme résultat la liste des régions. Comme c'est une liste potentiellement longue, il est pratique d'en imprimer juste des éléments.


length(reglist);         // pour connaitre le nombre d'elements
reglist[3].DisplayAll(); // imprime la 4ieme region (on peut indexer une liste)
tri-regs(reglist)[0].DisplayAll(); // imprime la region la plus grande

Beaucoup plus pratique: on peut interroger les régions en cliquant. C'est le rôle de la fonction describe-region. La fonction describe-region(etiq,point,pos) identifie une région à l'aide du point donné, trace son contour dans la sous-fenêtre pos, et affiche diverses propriétés du contour.

Autre exemple: séquence de commandes complètes pour construire les région définies par le signe du laplacien au niveau 6.


  // on suppose que mflap est construit: image-setup(current-iamge)
etiq = new RLEImage(mflap[6],0);
affiche-rle(etiq,2,200,0);
affiche-rle-cmap(rle,2);    // affichage avec table de couleur une-cmap
reglist = a-regions(etiq,10);
dd=describe-region(etiq,false,0);

On donne sur une page séparée un exemple complet avec des segmentations plus complexes, utilisant les codes-fonction 1 et 2.

Nous étudierons plus loin les classes et les fonctions utilisées pour la segmentation: RLEImage, hori_seg, pv_region, RegionProps, prop_vec.

Important:la gestion mémoire des classes pv_region et hori_seg.

Manipulation des régions d'une segmentation

La segmentation a tendance à produire un grand nombre de régions. Afin d'étudier en détail une telle segmentation, il existe de nombreuses fonctions pour afficher, trier, décrire les régions (voir les fichiers regf.scm, geom.scm). Ces manipulations passent le plus souvent par des listes (au sens Scheme).

La fonction describe-region(rle,point,pos) a été décrite plus haut. La fonction draw-region(id [,pos])affiche le centre, et le point SG de la région ayant l'identificateur id. Le paramètre id doit être une pv_region; dans l'exemple ci-cessus, c'est un élément d'une liste. On peut aussi trouver la région d'un point en cliquant, avec point2region(rle,point)

tri-regs(l) trie une liste de régions, selon la taille.

sort-big(l,s) trie, et conserve seulement les régions de taille supérieure à s.

Histogramme des régions

La fonction rle-histo(rle,regs) construit une paire d'histogrammes - le nombre de pixels pour chaque étiquette, et le nombre de régions pour chaque étiquette - qu'on peut ensuite afficher avec la fonction gnu-histo.


his=rle-histo(etiq,reglist);
gnu-histo car his;
gnu-histo cdr his;

RLEImage - image codée d'étiquettes

Une "étiquette" sera pour nous un entier, comme 0 pour négatif et 1 pour positif; raisonnablement, le nombre d'étiquettes sera toujours faible. Pour des raisons d'efficacité, nous avons choisi de représenter une image d'étiquettes sous forme codée: une suite de pixels identiques forme un "segment" - d'où le nom de la classe RLEImage pour Run Length Encoded Image. Le nombre de segments est petit par rapport au nombre de pixels (sinon les paramètres d'étiquettage sont mal choisis), ce qui assure une analyse de connexité rapide.

Critères d'étiquettage

Premier constructeur:
RLEImage(TBitmap* bmp,int i); !! a faire !
RLEImage(TBitmapFloat* bmp,int i);

Le deuxième argument de ce constructeur indique par un code le critère d'étiquettage:

  • 0 Les étiquettes sont 1 pour >=0.0, et 0 pour <0.0
  • 1 Les étiquettes sont dans [0,1,2,3] selon les intervalles [-infini,-eps,0.0,+eps,+infini]. La valeur de eps peut être positionnée à l'aide de la fonction seteps; la valeur initiale est 0.0 - les étiquettes sont alors 0 et 3.
  • 2 Fonction en escalier.

Paramètres de la fonction en escalier:


static int fnpred2(float x)   // signed "levels"
{
  int res= (int)(f3fac*x) / f3scal;
  return x>=0.0 ? res : res-1;
}
inline float setf3parms(float f,int n)
{ float f1=f3fac; f3fac=f; f3scal=256/n; return f1;}

Deuxième constructeur:
RLEImage(TBitmapABGR* src, TBitmapABGR* ref);

On compare l'image src à l'image "de reférence" ref; les étiquettes sont 0 (égalité) et 1 (différent).

Méthodes de la classe RLEImage


inline RLE_Line* GetLine(int y); inline int GetMin(); inline int GetMax(); inline int GetSizey(); inline int GetNbSegs(); int GetVal(int x,int y); bool Inside(int x,int y,pv_region* pr); void DisplayLine(int); void toABGR(TBitmapABGR& dst,int a,int b); void PseudoColor(TBitmapABGR& dst,TIntVector& cmap,int del=0); void CopySegs(TBitmapABGR& dst,TBitmapABGR& src);

toABGR(TBitmapABGR& dst,int a,int b)sert à l'affichage. On affiche val*a+b en fausses couleurs (jaune positif - vert négatif).

PseudoColor(TBitmapABGR& dst,TIntVector& cmap,int del=0)pour l'affichage avec une colormap (souvent plus pratique que la methode precedente). Le parametre del est utile s'il y a des etiquettes negatives: on translate toutes les valeurs de del, sachant que les indices de la colormap commencent a 0.

CopySegs(TBitmapABGR& dst,TBitmapABGR& src) copie avec masque.

Constructeurs - re-initialiseurs

A chaque constructeur correspond une methode "Encode" qui permet de re-initialiser le RLE; ainsi on evite des "new" dans une boucle d'analyse d'images


  inline RLEImage();
  RLEImage(TBitmapFloat* bmp,int i);
  RLEImage(TBitmapABGR* src, TBitmapABGR* ref);
  void EncodeROI(TBitmapABGR* src, TBitmapABGR* ref);
  void EncodeROIq(TBitmapABGR* src, TBitmapABGR* ref);
  void Encode(TBitmapFloat*src,int fx);
  void EncodeMorph(TBitmapFloat*src,int fx);
  int Morph(int x);
  //  RLEImage(TBitmapABGR* bmp,int (*pred)(int));
  // add 23-4-02
  RLEImage(int XI,int YI,int XF, int YF, int W, int H,
	   int(*fn)(int,int,void*),void* p);
  void EncodeROI(int XI,int YI,int XF, int YF,int(*fn)(int,int,void*),void* p);

La classe pv_région

Les propriétés calculées pour une région sont:

  • (ulx,uly) point supérieur gauche
  • id - un entier comme identificateur
  • size - taille en pixels
  • val - valeur de l'étiquette
  • nbholes - nombre de trous
  • xmin, xmax, ymax - bounding box (ymin = uly)
  • double sumx,sumy,sumxx,sumyy,sumxy;

Méthodes

  • get-ulx, get-uly, get-id etc. pour tous les champs ci-dessus
  • compute - calcul des moments
  • MeanX, MeanY
  • void CopyBox(TBitmapABGR& dst,TBitmapABGR& src) copie avec mask bounding box

La gestion mémoire des classes pv_region et hori_seg

Utiliser les procédures statiques

    hori_seg::PoolUsed()
    hori_seg::pool_init(size)
    hori_seg::pool_reinit()
     

pv_region::PoolUsed() pv_region::pool_init(size) pv_region::pool_reinit()

et les fonctions Scheme
  • pv_region-pool
  • pv_region-pool_ix
  • pv_region-pool_size

Les classes RegionProps et prop_vec

La classe RegionProps permet de calculer des propriétés supplémentaires pour les régions d'une segmentation.

Exemple:


   redi = current-image.GetRedImage();
   rpp = new RegionProps(rle,redi);
   rp1 = rpp.GetVec(1);
Dans cet exemple, rle est une RLEImage déjà segmentée. On calcule, pour toutes les régions, des propriétés par rapport à l'image redi qu'on peut interroger ensuite:

   rp1.Mean();   // moyenne des valeurs R de la région 1
   rp1.Max();    // le max

Plus exactement, une instance de la classe RegionProps attache une instance de prop_vec à chaque région. Les méthodes de prop_vec sont:


  inline int Size();
  inline double Mean();
  inline double StdDev();
  inline float Min();
  inline float Max(); 
  inline float AbsMax();
  inline void Reset();
  inline void Update(float);

C'est puissant: on peut construire autant de RegionProps différents que l'on veut; les méthodes


  void ReInit();
  void Compute(RLEImage& ima,TBitmap& bmp);
permettent de réactualiser les propriétés sans nouvelle allocation, comme c'est normalement le cas dans l'analyse d'une séquence d'images.

Remarque: le constructeur d'une RegionPropos alloue une place en fonction de la taille du pool des régions - si on change cette taille, il faut penser à ré-allouer les RegionProps.