Vous êtes sur la page 1sur 85

Java pour réseaux et image (JRI)

Cours 0 : Contenu et objectifs

Mohand Yazid SAIDI


Université Paris 13
France

M1 3IR
saidi@univ-paris13.fr

Plan
Introduction au JRI
Choix du langage java
Premier pas sous java
Conclusion
Introduction
Java pour réseaux et image
Objectifs et contenu
Bases de l’algorithmique
Structures de données
o Modélisation des réseaux
o Manipulation/traitement de l’image
Initiation à la programmation réseau
Calculs dans les réseaux
o Plus courts chemins
o Arbres couvrants
Communication réseau (partie supprimée pour manque de temps)
o Sockets
o RMI (Remote Method Invocation)
Image

Choix du langage java


Pourquoi choisir java ?
Java est un langage orienté objet (O.O.)
Bénéficier des avantages de la programmation orientée objet
Extensibilité, réutilisation et facilité de maintenance
Réflexion en termes de la structure du problème à résoudre plutôt
que de la structure d’ordinateur
Protabilité
Le code produit par un compilateur java peut être exécuté sur
n’importe qu’elle plateforme supportant une JVM
Gestion automatique et facile de la mémoire
Java gère la désallocation de la mémoire
API très riche
Succès (déploiement)
Android, navigateurs, etc.
Java : fonctionnement
Java est un langage de programmation interprété et
compilé

.java Compiler
.class .class
Interpreter Interpreter

Hello
Hello World
World

Linux
Windows

Java : fichier source


public class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello World");
}
}

Main : point d’entrée du programme


Elle doit être déclarée public et static
HelloWorld : Nom de la classe
La classe est déclarée avec le modificateur d’accès public
o Le nom du fichier source doit être HelloWorld.java
System.out.println : permet l’affichage de la chaine "Hello World"
passée en paramètre
Compilation d’un fichier source java
Commande
javac HelloWorld.java
Résultats de la commande
Pseudocode machine (bytecode)
Un fichier .class pour chaque classe déclarée dans le fichier source
Ici HelloWorld.class uniquement
Certains éditeurs (eclipse par exemple) exigent
d’organiser les fichiers sources en projets
Quelques uns permettent une compilation à la demande,
proposent des corrections et fournissent une aide en ligne

Interprétation et exécution du bytecode


Commande
Java HelloWorld
Interprètation et exécution du bytecode contenu dans le
fichier HelloWorld.class par la machine virtuelle (JVM)
Evidemment, la classe HelloWorld doit contenir une
méthode main ()
Résultat
Conclusion
Java est un langage de programmation
En JRI, c’en est qu’un outil pour
Mettre en oeuvre des algorithmes réseau
Manipuler et traiter des images

Java pour réseaux et image (JRI)


Cours 1 : Structures de données

Mohand Yazid SAIDI


Université Paris 13
France

M1 3IR
saidi@univ-paris13.fr
Plan
Introduction aux structures de données avancées
Files, piles et listes
Tables de hashage
Hashage double
Chaînage linéaire
Arbres
Arbres multibranches
Arbres linéaires
Arbres linéaires de recherche
Arbres rouge-noir et arbres AVL
Files de priorité (Heaps ou priority queues)
Conclusion

Structures de données
Stockent efficacement les données dans des
containers
Collections d’objets
Améliorent les performances
Temps d’accès (lecture et modification) et espaces mémoires réduits
En standard, java spécifie le comportement de diverses
structures de données
Plusieurs interfaces (List, Set, Map, Deque, Queue)
Framework Collections
Import java.util.*
Plusieurs implémentations sont fournies
ArrayList, HashSet, HashMap, TreeSet, TreeMap, LinkedList
Dans ce cours
Structures à étudier
Listes
Parcours séquentiels des élements de la liste
Tables de hashage
Accès direct et rapide (quasi-constant) aux éléments de la table
Arbres
Eléments ordonnés
Accès relativement rapide (logarithmique par rapport au nombre
d ’éléments) aux éléments

File
FIFO (First In First Out)
Exemple : file d’attente aux caisses d’un supermarché

Element supprimé Element dans la liste Element à ajouter

Insertion en queue de la file


Supression au début de la file
Deux implémentations
Tableau : le nombre maximum d’éléments dans la file est limité
Chainage : En plus de l ’élément, un champ permettant de
déterminer la référence du prochain élément est utilisé
Gestion des erreurs
Utilisation des exceptions
Pas d’espace mémoire
Impossibilité d’ajout ou de retrait d’élément
Paramètres invalides (taille inférieure à 0, etc.)
package jri ;

public class DataStructureException extends RuntimeException {

public DataStructureException () {
super () ;
}
public DataStructureException (String msg) {
super (msg) ;
}

Illustration
File implémentée sous forme de tableau
tab
head = 2 0
1
2
3
capacity = tab.length
4
tail = 7 5
6
7

count = tail - head si tail > head


= tail - head + capacity si tail < head
= capacity si la file est pleine
Interface Liste
package jri;

public interface Liste {

public boolean push (Object element) ;


public Object peek() ;
public Object pop() ;
public int size() ;
public boolean isEmpty() ;
public void clear() ;

Code
File implémentée sous forme de tableau
public class FixedList implements Iterable, Liste {
// champs
private Object[] tab;
private int head, tail, count;
// Constructeur
public FixedList(int capacity) {
if (capacity < 1)
throw new DataStructureException("Parameter error");
this.tab = new Object[capacity];
this.head = this.tail = this.count = 0;
}
public boolean push(Object element) throws DataStructureException {
if (count == tab.length)
throw new DataStructureException("Liste pleine");
if (element == null) return false;
tab[tail++] = element; ++count;
if (tail == tab.length) tail = 0;
return true;
}
Code (suite 1)
File implémentée sous forme de tableau
public boolean add(Object element) throws DataStructureException {
return this.push(element);
}

// retourner le premier objet (tete) sans l'effacer


public Object peek() {
if (count == 0) return null ;
return tab [head] ;
}

// retourner le premier objet (tete) après l'avoir effacé de liste


public Object pop() {
if (count == 0) throw new DataStructureException ("File vide");
Object result = tab [head] ;
tab [head] = null ; ++head ;
if (head == tab.length) head = 0 ;
--count ;
return result ;
}

Code (suite)
File : itérations et toString
protected class InternalIterator implements Iterator {
int elementIndex = 0;
public boolean isValid() { return elementIndex < count; }
public Object next() throws DataStructureException {
if (elementIndex >= count) throw
new DataStructureException("Iterateur invalide");
int index = head + elementIndex++ ;
if (index >= tab.length) index -= tab.length ;
return tab[index];
}
}
public Iterator iterator() { return new InternalIterator() ; }

public String toString() {


Iterator it = this.iterator();
if (!it.isValid()) return "[]";
String result = "[" + it.next();
while (it.isValid()) result += ", " + it.next();
return result + "]";
}
Code (suite et fin)
File implémentée sous forme de tableau
public int size() { return count ; }

public int getCapacity() { return tab.length ; }

public boolean isEmpty() {


if (count==0) return true ;
return false ;
}

public void clear() {


if (count == 0) {head = tail = 0; return ;}
do {
if (tail == 0) tail = tab.length - 1;
else --tail ;
tab [tail] = null ;
} while (tail != head) ;
count = head = tail = 0;
}
} // Fin de la classe FixedList

Pile
LIFO (Last In First Out)
Insertion en tête de la pile
Suppression en tête de la pile
Implémentation très semblable à celle de la file
empilement 4 4 dépilement

4
3 3
2 2
1 1
Illustration
Pile implémentée sous forme de tableau
A faire (exercice)
Un tableau de capacité = capacity
Un index pointant le dernier élément inséré
Plus simple à implémenter qu’une liste

Illustration
Pile implémentée sous forme chainée
Suite de maillons constitués chacun
D’un élément (l’objet)
D’une référence vers le prochain maillon
Maillon 1 Maillon 2 Maillon 3
(Iterator) (Iterator) (Iterator)

élément 1 next élément 2 next élément 3 null

Pile
Empilement
Dépilement
Interfaces pour les itérations dans une pile
// Fichier Iterator.java

package jri;
public interface Iterator {
public boolean isValid () ;
public Object next () ;
}

// Fichier StackIterator.java

package jri;
public interface StackIterator extends Iterator {}

// Fichier Iterable.java

package jri;
public interface Iterable {
public Iterator iterator () ;
}

Code
Pile chainée
package jri;
public class UnlimitedStack implements Iterable, Liste {

static protected class Maillon { // classe interne


protected Object element ;
protected Maillon next ;

protected Maillon (Object element, Maillon next){


this.element = element ;
this.next = next ;
}

protected Maillon (Maillon autreMaillon){


this.element = autreMaillon.element ;
this.next = autreMaillon.next ;
}
} // fin classe interne

public UnlimitedStack () {} // constructeur de la pile (tout à 0)


Code (suite 1)
Pile chainée
class InternalIterator implements StackIterator {// classe interne
Maillon current ;
protected InternalIterator () { current = maillon ; }

public boolean isValid() {


if (current == null) return false ;
return true ;
}

public Object next () {


if (current == null)
throw new DataStructureException ("Pas d'elements") ;
Object result = this.current.element ;
this.current = this.current.next ;
return result ;
}
} // fin classe interne
// ‘pile.iterator()’ permet de récupérer un itérateur sur ‘pile’
public StackIterator iterator() {return new InternalIterator () ;}

Code (suite 2)
Pile chainée
protected Maillon maillon ;
protected int count ;
public boolean push (Object element) {
if (element == null) return false;
this.maillon = new Maillon (element, this.maillon) ;
++count ;
return true ;
}
// retourner le premier objet (tete) sans l'effacer
public Object peek() {
if (count == 0) return null ;
return this.maillon.element ;
}
// retourner le 1er objet (tete) après l'avoir effacé de la liste
public Object pop() {
if (count == 0) throw new DataStructureException ("Pile vide") ;
Object result = this.maillon.element ;
--count ;
this.maillon = this.maillon.next ;
Code (suite et fin)
Pile chainée
return result ;
}

public int size() { return count ; }

public boolean isEmpty() {


if (count==0) return true ;
return false ;
}

public void clear() {


count = 0;
// this.maillon = null ; // ou bien
Maillon temporaire;
while ((temporaire = this.maillon) != null) {
this.maillon = this.maillon.next;
temporaire.next = null;
}
}
} // fin UnlimitedStack

Test de la pile
import jri.StackIterator;
import jri.UnlimitedStack;

public class UnlimitedStackTest {


public static void main (String args[]){
UnlimitedStack pile= new UnlimitedStack () ;
for (int i = 0 ; i<5 ; ++i) pile.push(i) ;
for (int i = 0 ; i<5 ; ++i) pile.pop() ;
for (int i = 0 ; i<10 ; ++i) pile.push(i) ;

System.out.println("Parcours de la pile avant tout pop()") ;


System.out.print("Pile [ ") ;
for (StackIterator it = pile.iterator() ; it. isValid () ; ) {
System.out.print(it.next()+ " ") ;
}
System.out.println(" ]");

pile.clear() ;
System.out.println() ;
Test de la pile (suite et fin)
for (int i = 0 ; i<5 ; ++i) pile.push(i) ;

System.out.print("Pile [ ") ;
for (StackIterator it = pile.iterator() ; it. isValid () ; ) {
System.out.print(it.next()+ " ") ;
}
System.out.println(" ]");
}
}

Liste générique doublement chainée


Maillon 1 Maillon 2 Maillon 3
élément 1 élément 2 élément 3

next next next = null


pred = null pred pred

Liste doublement chainée

Insertion à n’importe quelle position de la liste générique


Suppression de n’importe quel élément
Introduction aux tables de hashage
Tables d’association entre des clés et des valeurs
d’élément
Dictionnaire contenant des paires (clé, objet)
Exemples
Répertoire téléphonique
Clé = nom
Valeur = numéro de téléphone
Dictionnaire de langue française
Clé = mot français
Valeur = signification
Identification de voitures
Clé = numéro de chassis
Valeur = caractéristiques de la voiture (model, année de
fabrication, dénomination commerciale, etc.)

Table de hashage : utilité et illustration


Temps d’accès (lecture, modification ou
suppression) aux valeurs des clés très faibles
Souvent constants
Implémentée souvent sous forme de tables de listes
La clé détermine l’indice de l’entrée de la table qui contient (donc,
détermine la liste) contenant les valeurs de la clé

Hashcode = 4 ...
Hashcode = 3 ...
Hashcode = 2 ...
Hashcode = 1 ...
Hashcode = 0 ...
Table de hashage : principes
Une fonction de hashage h est appliquée à la clé k pour
déterminer un hash code = h (k)
Le hash code détermine l’indice e de la valeur v
associée à la clé k

Clés Valeurs associées


Hashcode = 4

Hashcode = 3
association
Hashcode = 2 Clé j valeur associée à la clé j
Hashcode = 1
Hashcode = 0 Clé i valeur associée à la clé j

Table de hashage : principes de hashage


A chaque valeur de clé est associé un hash code
hh hc
clé hashage grand nombre compression indice

Hash code (n) = h1 (n) = hc (hh(n))


h1 (n) est la fonction de hashage primaire
Attention, la fonction de hashage n’est pas injective
Exemple de hashage pour les entiers n
Hashcode = 4 9
hh (n) = n ; hc (n) = n % 5 ; h1 (n) = n % 5 ;
Hashcode = 3
Insertion de 9 à l’indice 9 % 5 = 4
Hashcode = 2
Insertion de 1 à l’indice 1 % 5 = 1
Hashcode = 1 1
Insertion de 5 à l’indice 5 % 5 = 0
Hashcode = 0 5
Insertion de 4 à l’indice 4 % 5 = 4
Clés
Collision !!!!
Table de hashage : Résoudre les collisions
Table de hashage à adressage ouvert
Utiliser une seconde fonction de hashage s’il y a collision
Hashage double : hc (h1(n)+i*h2(n)) avec
h2 (n) = n % 3 + 1 (fonction de hashage secondaire)
Rappel : h1 (4) = 4 (collision avec h1 (9) = 4) Hashcode = 4 9
Indice = ( h1 (n) + i * h2 (n) ) % 5 Hashcode = 3 4
h2 (4) = 4 % 3 + 1 = 2
Hashcode = 2
i = 1 : indice = (4+1*2)%5 = 1 Collision
Hashcode = 1 1
i = 2 : indice = (4+2*2)%5 = 3 Ok
Hashcode = 0 5
Suppression de la clé 9 (case d’indice 4)
Clés
Pourrait-on retrouver la clé 4 ?
o Marquer cette case comme libre (sans libérer sa mémoire)
Test linéaire: hc (h1(n)+i)
h2 ne dépend pas de la clé (ici, h2 = 1)

Table de hashage : Résoudre les collisions


Chaînage linéaire
Utiliser une liste chaînée pour stocker les valeurs
associées v à toute clé différente k

Hashcode = 4 (9, 9) (4, 4)

Hashcode = 3 null

Hashcode = 2 null

Hashcode = 1 (1, 1)

Hashcode = 0 (5, 5)
Table de hashage : précautions d’utilisation
Fonction de hashage
Toujours multiplier par un nombre premier
Pour pouvoir exploiter toutes les cases de la table de hashage
Choisir une fonction de hashage qui permet une
distribution uniforme des clés dans la table
Facteur de charge λ
λ = nombre de clés insérées / taille de la table de hashage
Table de hashage implémentée avec le double hashage
Si λ > 0.5 doubler la taille de la table
Table de hashage implémentée avec le chaînage linéaire
Si λ > 0.7 doubler la taille de la table

Implémentation
Interface HashTable
package jri;
public interface HashTable {

//Ajouter un objet ayant la clé key


public void put(int key, Object value) ;

//Récupérer l'objet de clé key


public Object get(int key) ;

//Supprimer l'objet de clé key


public void remove(int key) ;

//Vérifier si la table contient un élément dont la clé est key


public boolean contains(int key) ;

//Nombre de clés dans la table


public int size() ;

}
Interface pour les hashages primaire
et secondaire
// Fichier CollisionManager.java
package jri;

public interface CollisionManagement {


public int primaryHash (int key) ;
public int secondaryHash(int key, int i) ;
}

Classe implémentant les hashages primaire


et secondaire
// Fichier DoubleHash.java
package jri;
public class DoubleHash implements CollisionManagement {
final int hashTableSize ; // grand nombre premier
final int hashTableSizeMinus2 ;

public DoubleHash (int hashTableSize){


this.hashTableSize = hashTableSize ;
hashTableSizeMinus2 = hashTableSize - 2 ;
}

public int primaryHash (int key) { return key % hashTableSize ; }

public int secondaryHash(int previousHash, int key){


// return (primaryHash (key)+i*key%hashTableSizeMinus2+i)%ha.. ;
return (previousHash + 1 + key % hashTableSizeMinus2)
% hashTableSize ;
}
}
Implémentation de la table de hashage
utilisant le double hashage
package jri;
public class LinearHashTable {
//Bucket = iterator. Les itérateurs ne sont pas très utiles
//Ici, on accepte les objets null
private static class Bucket {
private int key;
private Object value;
private boolean free ;

public Bucket (int key, Object object) {


this.key = key ;
this.value = object ;
}
public void clean() { this.value = null ; free = true ; }

public void setKeyAndValue(int key, Object value) {


this.key = key ; this.value = value ; this.free = false ;
}
}

Implémentation de la table de hashage


utilisant le double hashage (suite 1)
private Bucket[] bucketArray; // Tableau de paires "clé-valeur"
private int count = 0; // Nombre d'éléments insérés
private CollisionManagement manager; // Gestionnaire de collisions

public LinearHashTable (int size) {


bucketArray = new Bucket [size] ;
manager = new DoubleHash (size) ;
}

public LinearHashTable (int size, CollisionManagement manager) {


bucketArray = new Bucket [size] ;
this.manager = manager ;
}
Implémentation de la table de hashage
utilisant le double hashage (suite 2)
private int getBucketIndex (int key) {
int index = manager.primaryHash(key) ;//Calcul hashcode primaire
int firstFreeBucket = index, i = 0 ;
while (bucketArray[index] != null) {
if (bucketArray[index].key == key) return index ;
// Se souvenir de la première case allouée mais libre
if (bucketArray[index].free == true) {
firstFreeBucket = index ; break ;}
//appliquer 2nd fonction de hashage pour résoudre la collision
index = manager.secondaryHash(index, key);
}
if (bucketArray[index] == null) return index ;
while (true) {
index = manager.secondaryHash(index, key);
if (bucketArray[index] == null) return firstFreeBucket ;
if (bucketArray[index].key == key) return index ;
// Evite les erreurs si table pleine mais qlq cases free
if (++i >= bucketArray.length) return firstFreeBucket ;
}
}

Implémentation de la table de hashage


utilisant le double hashage (suite 3)
public void put(int key, Object value)
throws DataStructureException {
if (bucketArray.length == count) // reste-il de la place ??
throw new DataStructureException ("Table pleine") ;
int index = getBucketIndex(key); // emplacement de clé
if(bucketArray[index] == null) { // Si l'emplacement est vide...
bucketArray[index] = new Bucket(key, value); ++count ;
}
else { // Si l'emplacement est déjà occupé ou libre...
if (bucketArray[index].free == true) ++count ;
// On remplace la valeur (choix d'implémentation)
bucketArray[index].setKeyAndValue(key, value);
}
}
public Object get(int key) throws DataStructureException {
if (count == 0) throw new DataStructureException("Table vide") ;
Bucket bucket = bucketArray[getBucketIndex(key)]; // Case dans
// laquelle devrait se trouver la clé
Implémentation de la table de hashage
utilisant le double hashage (suite 4)
if (bucket == null || bucket.free)
// Si la case est occupée, on a trouvé notre clé !
throw new DataStructureException ("Clé non trouvée");
else return bucket.value;
}
public void remove(int key) throws DataStructureException {
if (count == 0) // Si la table est vide, exception !!
throw new DataStructureException("Table vide");
Bucket del = bucketArray[getBucketIndex(key)];//case à nettoyer
if (del == null || del.free)
// Si la case est vide ou déjà nettoyée, on peut s'arrêter
throw new DataStructureException ("Clé non trouvée");
del.clean() ;
--count ;
}
public boolean contains(int key) {
Bucket bucket = bucketArray[getBucketIndex(key)];
return (bucket != null && !bucket.free);
}

Implémentation de la table de hashage


utilisant le double hashage (suite et fin)
// Mettre en private au lieu de public
//utile ici pour les tests uniquement
public Bucket elementAt (int i){
if (i < 0 || i >= this.bucketArray.length)
throw new DataStructureException ("Index hors de la table") ;
return this.bucketArray [i] ;
}

public int size() {


return count ;
}

public int capacity() {


return bucketArray.length ;
}
}
Exemple d’utilisation d’une table de hashage
import jri.LinearHashTable ;
public class TestLinearHashTable {
public static void main(String[] args) {
LinearHashTable table = new LinearHashTable (5) ;
table.put(9, 9) ;
table.put(1, 1) ;
table.put(5, 5) ;
table.put(4, 4) ;
System.out.println("Affichage de la table avant d'effacer 9") ;
for (int i = 0; i < table.capacity() ; ++i)
System.out.println("case "+i+" : "+table.elementAt(i)) ;
table.remove(9) ;
System.out.println("\n\nAffichage table après avoir effacé 9") ;
System.out.println("\n\nLa table de hashage, contient elle "
+"la valeur 4 ? "+table.contains(4)) ;
for (int i = 0; i < table.capacity() ; ++i)
System.out.println("case "+i+" : "+table.elementAt(i)) ;
table.put(0, 0) ;
System.out.println("\n\nAffichage table après avoir ajouté 0") ;

Exemple d’utilisation d’une table de hashage


(Suite et fin)
for (int i = 0; i < table.capacity() ; ++i)
System.out.println("case "+i+" : "+table.elementAt(i)) ;
table.put(5, 5) ;
System.out.println("\n\nAffichage table après avoir ajouté 5") ;
for (int i = 0; i < table.capacity() ; ++i)
System.out.println("case "+i+" : "+table.elementAt(i)) ;
table.remove(3) ;
System.out.println("\n\nAffichage table après avoir effacé 3") ;
for (int i = 0; i < table.capacity() ; ++i)
System.out.println("case "+i+" : "+table.elementAt(i)) ;

table.put(2, 2) ;

System.out.println("\n\nAffichage table après ajouté 2") ;


for (int i = 0; i < table.capacity() ; ++i)
System.out.println("case "+i+" : "+table.elementAt(i)) ;
}
}
Aller plus loin
Table de hashage
La table de hashage chaînée ne requiert q’une
seule fonction de hashage (hashage primaire)
Déterminer l’indice de l’entrée de la table contenant la liste
où sera trouvé/recherché/supprimé le nouvel élément
Une opération sur la liste correspondante (recherche,
insertion ou suppression)
Pour de bonnes performances
Les clés doivent être (le plus possible) uniformément
disribuées sur les entrées de la table de hashage
Le hashage primaire doit utiliser une fonction de compression basée
sur le modulo d’un nombre premier p
Le hashage secondaire doit utiliser une fonction de compression
basée sur le modulo d’un nombre premier très légèrement inférieur
à p (généralement p-2)

Arbres
Structure de données permettant de représenter
des informations hiérarchisées
Exemples
Arbre généalogique, expression régulière, etc.
Un arbre est constitué d’un ensemble de noeuds liés
entre eux par des arêtes
Tout noeud (à l’exception de la racine) B racine
a un seul père et peut avoir
0, 1 ou plusieurs fils
A C D
Un noeud ne possédant pas
de fils est appelé noeud feuille
feuilles E F G
Types d’arbres
B
Deux catégories
Arbres binaires
A D
Deux fils au maximum : gauche et droite
Arbres multibranches C E F
Pas de contraintes sur
le nombres de fils Arbre binaire
Peuvent être représentés
par des arbres binaires B
Deux fonctionalités
Stockage hiérarchisé A C D
XML, expressions régulières, etc.
Recherche rapide E F
Dictionnaire, plus courtes distances, etc. Arbre multibranches

Arbres binaires de recherche


Les noeuds sont ordonnés selon leurs priorités
Une priorité est associée à tout noeud
La priorité de tout noeud est inférieure (ou égale) à la
priorité de son noeud fils de gauche
La priorité de tout noeud est supérieure à la priorité de son
noeud fils de droite
Pour comparer les priorités des noeuds, il sera judicieux
de définir une interface Compare comme suit :
package jri;
public interface Compare {
// -1 if priority (obj1) <= priority (obj2), 0 if
// priority (obj1) == priority (obj2), 1 otherwise
public int compare(Object obj1, Object obj2);
}
Implémentation des arbres binaires
Classe pour la comparaison des éléments
import jri.Compare ;

public class CompareManager implements Compare {

public boolean compare(Object obj1, Object obj2) {


return ((Comparable<Object>) obj1).compareTo(obj2);
}

Interface pour le parcours de l’arbre


package jri;
public interface BinaryTreeIterator extends Iterator {
// Aller au début de l'arbre
void first () ;

// Aller a la fin de l'arbre


void end () ;

// Retourner l'objet courant et aller au noeud précédent


public Object prev () ;

// return the element referenced by current


public Object element () ;

// Methodes héritées
// public boolean isValid () ; // public void next () ;
}
Arbres binaires
Avant d’aller plus loin
Voir comment parcourir un arbre binaire de
recherche
Itérateur
Le plus petit élément
Le successeur
Le prédécesseur
Voir comment sont construits et modifiés les arbres
binaires de recherche
Les insertions
Les suppressions

Parcours de l’arbre
du plus petit au plus grand
Iterator.next ()

Référence vers le noeud


racine (root) 20
Arbre.iterator ()
18 21

13 22

Iterator.first () 2 16 25

5 14 17

15 Iterator.end ()
Construction d’arbres binaires : gestion des
insertions
Initialement : aucun noeud 20 Insertion de 20
20 devient root car
Insertion de 18 18 21 aucun root
Insertion deavant
21

Insertion de 13 13 22 Insertion de 22

2
Insertion de 16 2 16 25 Insertion de 25

5
17
Insertion de 14 5 14 17

15
Insertion de 15

L’élément inséré est toujours une feuille

Construction d’arbres binaires : gestion des


suppressions (cas 1)

Suppression d’un noeud n’ayant pas deux fils


Exemple : noeud 21
Coller le fils de 21 (s’il y en a un) au noeud père de 21
20 20

18 21 18 21

13 22 13 22

2 16 25 2 16 25

5 17 5 17
Construction d’arbres binaires : gestion des
suppressions (cas 2)
Suppression d’un noeud ayant deux fils (ex. Noeud 13)
Déterminer le successeur de 13 (le noeud le plus à gauche des
descendants de son fils de droite. Evidemment, ce noeud ne peut pas
avoir de fils gauche) successeur = 14
20 20

18 21 18 21

13 22 13 22

2 16 25 2 16 25

5 14 17 14 5 14 17

15 successeur (13) 15

Implémentation des arbres binaires


Les noeuds
package jri;
public class BinaryTree implements Iterable {
protected class Node {
protected Node left, right, parent ;
Object element ;
//pour construire un noeud ‘‘sentinelle’’
protected Node () {
left = right = this ;
parent = null ;
element = null ;
}
//Construire un noeud contenant un élément
protected Node (Object element) {
left = right = parent = SENTINEL ;
this.element = element ;
}
} // fin classe Node
Implémentation des arbres binaires
Classe d’implémentation des itérateurs
protected class BinaryTreeIteratorImpl
implements BinaryTreeIterator {
protected Node current ;
protected BinaryTreeIteratorImpl () { first () ; }

protected BinaryTreeIteratorImpl (Node currentNode) {


current = currentNode ;
}

public void first () { current = minimum (root) ;}

public void end () { current = maximum (root) ; }

public Object next () {


//optionnellement, vérifier si l'itérateur est valide
Object result = current.element ;
current = successor (current) ;

Implémentation des arbres binaires (suite)


Classe d’implémentation des itérateurs
return result ;
}

public Object prev () {


//optionnellement, vérifier si l'itérateur est valide
Object result = current.element ;
current = predecessor (current) ;
return result ;
}

public boolean isValid () {


return current != SENTINEL ;
}

public Object element () { return current.element ; }


} // fin de la classe BinaryTreeIteratorImpl
Implémentation des arbres binaires
La classe BinaryTree
//// champs de la classe BinaryTree
protected Node SENTINEL = new Node () ;
protected BinaryTreeIteratorImpl SENTINEL_IT
= new BinaryTreeIteratorImpl(SENTINEL);
protected Node root ;
protected Compare compare ;
protected int count ;

public BinaryTree (Compare compare){


this.compare = compare ; root = SENTINEL ; count = 0 ;
}
public int size () { return count ; }
//méthodes utilitaires
protected Node minimum (Node node) {
while (node.left != SENTINEL) node = node.left;
return node;
}

Implémentation des arbres binaires


La classe BinaryTree (suite 1)
protected Node maximum(Node node) {
// if (node != SENTINEL) //inutile
while (node.right != SENTINEL) node = node.right;
return node;
}

protected Node predecessor (Node node){


if (node.left != SENTINEL) return maximum (node.left) ;
Node predecessorChild=node, predecessor=node.parent;

while ( (predecessor != SENTINEL)


&& (predecessorChild == predecessor.left)){
predecessorChild = predecessor ;
predecessor = predecessor.parent ;
}
return predecessor ;
}
Implémentation des arbres binaires
La classe BinaryTree (suite 2)
protected Node successor (Node node){
if (node.right != SENTINEL) return minimum (node.right);
Node successorChild = node, successor = node.parent;

while ( (successor != SENTINEL)


&& (successorChild == successor.right)){
successorChild = successor ;
successor = successor.parent ;
}
return successor ;
}

Implémentation des arbres binaires


La classe BinaryTree (suite 3)
protected BinaryTreeIterator search (Object element){
if (element == null)
return SENTINEL_IT ;
Node node = root;
int compareResult;
while (node != SENTINEL
&& (compareResult = compare.compare(element,
node.element)) != 0) {
if (compareResult < 0) node = node.left;
else node = node.right;
}

if (node == SENTINEL) return SENTINEL_IT ;

return new BinaryTreeIteratorImpl (node) ;


}
Implémentation des arbres binaires
La classe BinaryTree (suite 4)
public BinaryTreeIterator add (Object element){
if (element == null) return SENTINEL_IT ;
Node parent = SENTINEL ;
Node x = root ;
int compareResult ;

while (x != SENTINEL){
parent = x ;
compareResult = compare.compare(element, x.element) ;
//empecher doublons = décommentez les 2 lignes svt
//if (compareResult == 0) return null ;
//else
if (compareResult <= 0) x = x.left ;
else x = x.right ;
}
++count ;

Implémentation des arbres binaires


La classe BinaryTree (suite 5)
x = new Node (element) ;
x.parent = parent ;
if (parent == SENTINEL) root = x ;
else
if (compare.compare(x.element, parent.element) <= 0)
parent.left = x ;
else parent.right = x ;
return new BinaryTreeIteratorImpl (x) ;
}
Implémentation des arbres binaires
La classe BinaryTree (suite 6)
public void remove (Object element){
if (element == null) return ;

BinaryTreeIterator iterator = search (element) ;


Node deletedNode
= ((BinaryTreeIteratorImpl) iterator).current ;

if (deletedNode == SENTINEL) throw new


DataStructureException ("Aucun élément à supprimer");

--count ;
Node succChild, succ ;
if (deletedNode.left == SENTINEL
|| deletedNode.right == SENTINEL)
succ = deletedNode ;
else succ = successor (deletedNode) ;

Implémentation des arbres binaires


La classe BinaryTree (suite 7)
// determiner le noeud accueillant la sous branche de
// succ à deplacer vers un fils de succ
if (succ.left == SENTINEL) //successor est 1 descendant
succChild = succ.right ;
else succChild = succ.left ;

//coller le fils du noeud suppr au pere du noeud suppr


if (succChild != SENTINEL)
succChild.parent = succ.parent ;

if (succ.parent == SENTINEL) root = succChild ;


else {
//mettre à jour le fils de succ.parent.(left||right)
if (succ == succ.parent.left)
succ.parent.left = succChild ;
else succ.parent.right = succChild ;
}
Implémentation des arbres binaires
La classe BinaryTree (suite 8)
// remplacer l'element de deletedNode par celui de succ
// s'il le faut
if (succ != deletedNode)
deletedNode.element = succ.element ;
}

public BinaryTreeIterator iterator () {


return new BinaryTreeIteratorImpl (root) ;
}
public String toString() {
if (root==SENTINEL) return "Arbre sans aucun noeud" ;
String decalage = " " ;
return root.element.toString () +"\n"
+ toString (root.left, decalage)
+ toString (root.right, decalage) ;
}

Implémentation des arbres binaires


La classe BinaryTree (suite et fin)
public String toString(Node current, String decalage) {
if (rootNode == SENTINEL) return decalage + "-\n";
String dec = decalage + " ";
return decalage + rootNode.element.toString() + "\n"
+ toString(rootNode.prev, dec)
+ toString(rootNode.next, dec);
}
} // Fin classe BinaryTree
Exemple d’utilisation d’un arbre binaire
de recherche
import jri.BinaryTree;
import jri.BinaryTreeIterator;
public class BinaryTreeTest {
public static void main(String[] args) {
BinaryTree binaryTree
= new BinaryTree (new CompareManager ()) ;
binaryTree.add(20) ;
binaryTree.add(21) ;
binaryTree.add(22) ;
binaryTree.add(18) ;
binaryTree.add(15) ;
binaryTree.add(17) ;
binaryTree.add(16) ;
binaryTree.add(2) ;
binaryTree.add(5) ;
binaryTree.add(25) ;
binaryTree.add(14) ;

Exemple d’utilisation d’un arbre binaire


de recherche (suite 1)
binaryTree.add(15) ;

Object integer2Print=null ;
BinaryTreeIterator it = binaryTree.iterator () ;
it.first() ;
System.out.print("Arbre a "+binaryTree.size()
+" éléments [ ") ;
while (it.isValid ()) {
integer2Print = it.next () ;
System.out.print(integer2Print+ " ") ;
}
System.out.println("]") ;

System.out.println("\n\n"+binaryTree+"\n") ;

binaryTree.remove(17) ;
binaryTree.remove(15) ;
Exemple d’utilisation d’un arbre binaire
de recherche (suite et fin)
it.first() ;
System.out.print("Arbre à "+binaryTree.size()
+" éléments [ ") ;
while (it.isValid ()) {
integer2Print = it.next () ;
System.out.print(integer2Print+ " ") ;
}
System.out.println("]") ;

System.out.println("\n\n"+binaryTree+"\n") ;

} // Fin de la classe BinaryTeeTest

Résultats d’exécution du programme


BinaryTreeTest
Avantages et limites des arbres binaires
de recherche
Ordonne les données au fur et à mesure qu’elles
arrivent
Très utile pour les applications nécessitant une
réoganisation rapide des informations dynamiques
Temps d’accès logarithmique si l’arbre est équilibré
Par contre, les arbres de recherche souffrent de
problème d’équilibre de poids
L’ordre des insertions et suppressions influence sur les
temps d’accès (lecture ou modification)
Solution : équilibrer l’arbre à chaque insertion ou
suppression d’une donnée
Arbres red-black et arbres AVL

Arbres binaires équilibrés-déséquilibrés


Exemples
Ordre des insertions 2 Ordre des insertions
dans l’arbre bleu 5 dans l’arbre rouge
18, 14, 5, 16, 22, 20, 21, 13 2, 5, 13, 14, 15, 16, 17,
25, 15, 17, 2, 13 14 18, 20, 21, 22, 25
4 recherches au max 12 recherches au max
15
18 16
17
14 22 18
20
5 16 20 25
21
2 13 15 17 21 22
25
Comment équilibrer un arbre binaire ?
Rotation vers la gauche
20 20
23 23
18 18
21 25 21 25
19 14 19
5 22 27 22 27
5
20 16 20
2 14

13 2 13 17
16

17

L’insertion de 17 Arbre équilibré


déséquilibre l’arbre

Comment équilibrer un arbre binaire ?


Rotation vers la droite

20 20
18 21 21
14

14 25 22 22
5 18

5 16
13 16 25

13

L’insertion de 13 Arbre équilibré


déséquilibre l’arbre
Red-black Trees
Arbres rouge noir
Arbres quasi-équilibrés
Tout chemin de la racine vers une feuille est au maximum
2 fois plus grand que le plus chemin reliant la source à une
feuille
Complexité d’accès O (log (N))
Cinq règles doivent être toujours vérifiées
Un noeud est soit rouge, soit noir (pas les deux)
Toutes les feuilles sont noires (feuille = noeud fictif)
Tous les noeuds fils d’un noeud rouge sont noirs
Tout chemin reliant un noeud donné à un de ses noeuds
feuille et descendant doit contenir le même nombre de
noeuds noirs

Exemple d’arbre binaire red-black


Racine : elle peut toujours
23 être colorée en noir

18 26

13 20 25 28

2 16 19 22

5 14 17

15 Noeud fictif (null)


Arbres AVL
Arbres équilibrés
A chaque noeud est associé un facteur d’équilibre
Valeur positive, nulle ou négative
Le facteur d’équilibre correspond à la hauteur de la plus grande
branche du sous-arbre de droite moins la plus haute branche du
sous-arbre de gauche
Un sous-arbre est équilibré si tous ses sous-arbres sont équilibrés
Les valeurs -1 et 1 sont tolérées car il n’est pas toujours possible
d’avoir un arbre parfaitement équilibré
o Un arbre dont le nombre de noeuds est impair doit peser à
droite (+1) ou à gauche (-1)
L’équilibrage de l’arbre se fait pendant la construction (insertions et
suppressions)
Faire des rotations pour équilibrer tous les sous-arbres dont le
facteur d’équilibre est passé à 2 ou -2

Exemple d’arbre binaire


Calcul du facteur d’équilibre
-1
20

Déséquilbre car |-2| > 1 -2 0


17 23

0 0 -1 -1
13 18 22 25

+1 0 0 0
2 15 21 24

0 0 0
5 14 16

Arbre AVL déséquilibré


Exemple d’arbre binaire
Calcul du facteur d’équilibre
-1
20

+1 0
13 23

+1 -1 -1 -1
2 17 22 25

0 0 0 0 0
5 15 18 21 24

0 0
14 16

Arbre AVL équilibré

Exemple d’arbre binaire


Calcul du facteur d’équilibre
20

13 23

2 17 22 25

5 15 18 21 24

16
La file de priorité
Priority Queue ou Heap
Arbre binaire vérifiant
La racine est toujours le plus petit élément (haute priorité)
Evidemment, on pourra choisir une autre implémentation dans
laquelle la racine correspond toujours au plus grand élément
Chaque niveau de l’arbre doit être complètement rempli
avec les éléments avant de passer au niveau suivant
La suppression concerne toujours le noeud racine
Une file de priorité assure qu’un événement de priorité
supérieure est traité avant un autre de priorité inférieure
Peut être implémentée avec un arbre binaire ou un tableau

Exemple de création d’une file de priorité

2 2 2 2 2

15 15 5 15 5 15 5

17 17 16
2 2

15 5 15 5

17 16 18 17 16 18 14
0 1 2 3 4 5 6 7
2 15 5 17 16 18 14
File de priorité : quelques règles
Tout noeud dans l’arbre est inférieur à ses deux fils
La racine de l’arbre correspond donc au minimum
Si la file de priorité est implémentée avec une table
Le noeud fils gauche d’un noeud d’indice i se trouve à la
position 2*i
Le noeud fils de droite d’un noeud d’indice i se trouve à la
position 2*i+1
Le noeud père du noeud d’indice i se trouve à la position
partie_entière (i / 2)

Insertion d’un élément


2 2 2

15 5 15 5 15 5

17 16 18 14 17 16 18 14 1 16 18 14

1 17

2 1

1 5 2 5

15 16 18 14 15 16 18 14

17 17
Algorithme d’insertion d’un élément
La position du premier noeud feuille libre est connue
1. Ajouter le noeud sur la première position libre
2. Incrémenter de 1 le nombre de noeuds de l’arbre count
3. Aller à l’étape 7 si le noeud ajouté correspond à la racine
4. Comparer le noeud ajouté à son noeud père
5. Si le noeud ajouté est inférieur au noeud père
Echanger les positions des deux noeuds
6. Aller à l’étape 3
7. Fin

Suppression du noeud racine


1 17

5 2 5 2

15 16 18 14 15 16 18 14

17

2 2

5 17 5 14

15 16 18 14 15 16 18 17
Algorithme de suppression du noeud racine
La position de la racine est connue et est stockée dans
un champ
1. Si le nombre de noeuds de l’arbre count est égal à zéro
Aller à l’étape 6 (déclencher une exception)
2. Sinon décrémenter count de 1
3. Remplacer le noeud racine par le dernier noeud n de
l’arbre (le noeud feuille le plus à droite)
4. Comparer le noeud n avec son plus petit noeud fils p
5. Si le noeud n est supérieur à ce noeud fils p alors
Echanger les positions des deux noeuds n et p
Aller à l’étape 4
6. Fin

File de priorité
A titre d’exercice, implémenter les files de priorité en
utilisant un tableau
Conclusion
Les structures de données sont efficaces pour stocker et
organiser des données
Accès (lecture et modification) rapide aux données
Tri des données
Souvent, il est possible d’implémenter les structures de
données en utilisant
Des tableaux ou des références
Il est possible de parcourir tous les éléments de toute
structure de données
Attention : certains parcours (itérateurs) sont coûteux et ne
garantissent pas le même ordre de parcours
Pour de meilleures performances, il pourrait être
judicieux de combiner différents types de structures

Java pour réseaux et image (JRI)


Cours 2 : Calcul de routes

Mohand Yazid SAIDI


Université Paris 13
France

M1 3IR
saidi@univ-paris13.fr
Plan
Introduction aux Routage
Plus courts chemins
Algorithme de Bellman-Ford
Algorithme de Dijkstra
Arbre couvrant minimal
Algorithme de Kruskal

Introduction au routage
Permet l’acheminement des objets d’un point à
l’autre
Collecte des données sur le réseau (routier, informatique,
télécom, etc.)
Calcul de routes
Dans ce chapitre, nous nous intéresserons au module
de calcul de routes (path computation)
Route d’une source
Vers une seule destination
Algorithme des plus courts chemins
Vers plusieurs destinations
Arbre couvrant minimal
Domaines d’application du routage
Réseaux informatiques
Routage unicast
OSPF, IS-IS (shortest Path First, algorithme de Dijkstra)
RIP (Distance Vectors, Algorithme de Bellman-Ford)
Routage multicast
MOSPF, PIM, DVMRP
Diffusion
STP
Réseaux routiers
Réseaux télécoms

Module de calcul de routes


(Path computation)
Déterminer les routes permettant de relier un
ensemble de noeuds
Routage aléatoire
Acheminement par inondation, acheminement aléatoire
(stochastique ou aléatoire)
Routage basé sur les plus courts chemins
Shortest path first
Routage multi-constraint
Optimiser plusieurs métriques et/ou vérifier plusieurs contraintes
Routage compact
Routage hiérarchique
Etc.
Dans ce cours
Routes point à point
Algorithme des plus courts chemin
Vecteurs de distances
Bellman-Ford
Etats de liens
Dijkstra
Routes point à multipoint
Algorithme de Kruskal
Algorithme de Prim

Algorithmes des plus courts chemins


Description du problème
Entrée :
un graphe orienté G = (V, E, w)
V : ensemble des noeuds,
o |V| = n (nombre de noeuds du graphe)
E : ensemble des arcs (orientés)
o |E| = m (nombre d’arcs du graphe)
w : fonction poids associé à chaque arc appartenant à E
o w(u,v) Représente le coût ou poids de l’arc (u, v)
Sortie : plus court chemin πσ (s, t) et/ou plus courte distance
distσ (s, t) entre deux noeuds source (s) et destination (t)
Résolution du problème
des plus courts chemins
Trois cas
Graphe avec cycles de coût négatif
Pas de plus court chemin
Graphe sans cycles de coût négatif mais avec arcs de
poids négatif
Algorithme de Bellman-Ford
Graphe sans arcs de poids négatif
Algorithmes de Dijkstra et Bellman-Ford

Propriétés des plus courts chemins


distσ (s, s) = 0 ∀s ∈ V
distσ (u1, ui+1) = Min (distσ (u1, ui) + w(ui,ui+1))
ui ∈ V
∀ (u1, ui+1) ∈ V2

Formule récursive (base de l’algorithme de Bellman-Ford)


Algorithme de Bellman-Ford
Bellman-Ford (s, t)
Initialisation (étape 0)
dist [t] = 0 et ∀ u ∈ V − {t} : dist [u] = ∞
Itérations (étapes 1 à n - 1)
Pour i = 1, . . . , n − 1 faire
Pour (u, v) ∈ E faire
o dist [u] = Min (dist [u], dist [v] + w(u, v)))
o Si dist [u] == dist [v] + w(u, v) alors suiv [u] = v Finsi
Fin-Pour
Fin-Pour
Renvoyer dist [s]

Algorithmes de Bellman-Ford
Exemple
s
1
1 1
2 3
4 1 3
4 5 t
1

1 2 3 4 5
Étape 0 ∞ ∞ ∞ ∞ 0null
Étape 1 ∞ ∞ 35 15 0null
Étape 2 43 54 24 15 0null
Étape 3 33 54 24 15 0null
Étape 4 33 41 24 15 0null
Complexité de l’algorithme de Bellman-Ford
Etape 0
n opérations
Etape i (0 < i <= n-1)
m opérations
Nombre d’opérations total
n + (n-1).m
Complexité O (nm)

Idée de l’algorithme de Dijkstra


Plus rapide mais il n’est applicable que lorsque les coût
des liens sont positifs
A l’étape i (0 < i <= n-1), n’explorer que les liens
adjacents au (i-1)ème plus proche noeud
Idée : ordonner les noeuds selon leur distance de la
destination
Algorithme de Dijkstra
Dijkstra (s, t)
Initialisation (étape 0)
dist [t] = 0 et ∀ u ∈ V − {t} : dist [u] = ∞
Initialiser tous les noeuds à non marqué
Tant qu’il existe un sommet non marqué faire
Marquer le noeud u non marqué qui a la plus petite distance vers t
Si u == s alors Renvoyer dist [s]
Pour chaque arc (v, u) tel que le noeud v est non marqué faire
dist [v] = Min (dist [v], dist [u] + w(v, u))
Si dist [v] == dist [u] + w(v, u) alors suiv [v] = u Finsi
Fin-Pour
Fin-Tantque
Renvoyer dist [s]

Algorithme de Dijkstra
distance ∞
Marque
noeud 1 ∞
s
noeud 2 ∞
1
noeud 3 ∞ 1 1
noeud 4 ∞ 2 3
noeud 5 0null 4 1 3
4 5 t
1

Noeud en traitement (5, 0)

(5, 0)
Algorithme de Dijkstra
distance ∞ ∞
Marque
noeud 1 ∞ ∞
s
noeud 2 ∞ ∞
1
noeud 3 ∞ 35 1 1
noeud 4 ∞ 15 2 3
noeud 5 0null 0null 4 1 3
4 5 t
1

Noeud en traitement (4, 1)

(4, 1) (3, 3)

Algorithme de Dijkstra
distance ∞ ∞ ∞
Marque
noeud 1 ∞ ∞ ∞
s
noeud 2 ∞ ∞ 54
1
noeud 3 ∞ 35 24 1 1
noeud 4 ∞ 15 15 2 3
noeud 5 0null 0null 0null 4 1 3
4 5 t
1

Noeud en traitement (3, 2)

(3, 2) (2, 5)
Algorithme de Dijkstra
distance ∞ ∞ ∞ ∞
Marque
noeud 1 ∞ ∞ ∞ 33
s
noeud 2 ∞ ∞ 54 54
1
noeud 3 ∞ 35 24 24 1 1
noeud 4 ∞ 15 15 15 2 3
noeud 5 0null 0null 0null 0null 4 1 3
4 5 t
1

Noeud en traitement (1, 3)

(1, 3) (2, 5)

Algorithme de Dijkstra
distance ∞ ∞ ∞ ∞ ∞
Marque
noeud 1 ∞ ∞ ∞ 33 33
s
noeud 2 ∞ ∞ 54 54 41
1
noeud 3 ∞ 35 24 24 24 1 1
noeud 4 ∞ 15 15 15 15 2 3
noeud 5 0null 0null 0null 0null 0null 4 1 3
4 5 t
1

Noeud en traitement (2, 4)

(2, 4)
Algorithme de Dijkstra
distance ∞ ∞ ∞ ∞ ∞ ∞
Marque
noeud 1 ∞ ∞ ∞ 33 33 33
s
noeud 2 ∞ ∞ 54 54 41 41
1
noeud 3 ∞ 35 24 24 24 24 1 1
noeud 4 ∞ 15 15 15 15 15 2 3
noeud 5 0null 0null 0null 0null 0null 0null 4 1 3
4 5 t
1

Aucun noeud à traiter fin

Complexité de l’algorithme de Dijkstra


Etape 0
n opérations
Etape i (0 < i <= n-1)
m opérations de comparaison au total
Log (n) pour les insertions dans la file de priorité
Nombre d’opérations total
n + m + (n-1).log(n)
Complexité O ( m + n.log(n) )
Arbre couvrant minimal
Arbre couvrant minimal (Minimum Spanning Tree ou
MST)
S’applique à un graphe non orienté, connexe et valué
Un MST est un arbre
traversant tous les sommets du graphe
dont la somme des poids de ses liens est minimale
plusieurs algorithmes de calcul dont la complexité est pôlynomiale
Lorsque l’ensemble des noeuds à couvrir est un sous-
ensemble des noeuds du graphe (contenant plus de 2
noeuds), l’arbre couvrant minimal est un arbre de Steiner
Le problème de calcul d’un arbre de Steiner est un
problème NP-difficile

Exemple d’un arbre couvrant minimal


1
1 1
2 1 3
4 3
4 5
1
3 2 2
1
6
7 8
2 2
Algorithmes de calcul de l’arbre couvrant
minimal
Algorithmes
Algorithme de Borůvka
Premier algorithme
Publié en 1926 dans un article intitulé : ”O jistém problému
minimálním” ou ”sur un certain problème minimal”
Basé sur le compactage des noeuds
Algorithme de Prim
Algorithme de Kruskal
Application
Réseaux électriques, informatiques et télécoms
Couvrir une zone avec un minimum coût

Algorithme de Prim
T←∅
A ← {a0}
a0 peut être n’importe quel noeud du graphe
Tant que |T| < n − 1 faire
Calculer l’arête e = (a, b) (avec a ∈ A et b ∉ A) de coût
minimal
T ← T ∪ {e}
A ← A ∪ {b}
Fin-Tantque
Retourner T
Exécution de l’algorithme de Prim
1
1 1
2 1 3
4 3
4 5
1
3 2 2
1
6
7 8
2 2

Algorithme de Kruskal
Soit L une liste triée des arêtes du graphe G
La liste est triée suivant les poids des arêtes, du plus faible
au plus grand
T←∅
Tant que |T| < n − 1 et L ≠ ∅ faire
e = (a, b) ← retrait_premier_élément (L)
Si composanteConnexe(a) ≠ composanteConnexe(b) alors
T ← T ∪ {e}
Finsi
Fin-Tantque
Retourner T
Exécution de l’algorithme de Kruskal
1 1 1
1 1 1 1 1 1
2 1 3 2 1 3 2 1 3
4 3 4 3 4 3
4 5 4 5 4 5
1 1 1
3 2 2 3 2 2 3 2 2
1 1 1
6 6 6
7 8 7 8 7 8
2 2 2 2 2 2

Exécution de l’algorithme de Kruskal


1 1 1
1 1 1 1 1 1
2 1 3 2 1 3 2 1 3
4 3 4 3 4 3
4 5 4 5 4 5
1 1 1
3 2 2 3 2 2 3 2 2
1 1 1
6 6 6
7 8 7 8 7 8
2 2 2 2 2 2
Exécution de l’algorithme de Kruskal
1
1 1
2 1 3
4 3
4 5
1
3 2 2
1
6
7 8
2 2

Conclusion
Arbre des plus courts chemins
Graphe orienté ou non orienté
Poids positifs ou nuls
Dijkstra (et/ou Bellman-Ford)
Poids négatifs sans cycles de coût négatif
Belmann-Ford
Arbre couvrant minimal
Graphe non orienté
Algorithme de Prim
Algorithme de Kruskal
Java pour réseaux et image (JRI)
Cours 3 : Traitement d’image
sous java (API java2D)

Mohand Yazid SAIDI


Université Paris 13
France

M1 3IR
saidi@univ-paris13.fr

Introduction
La quasi-totalité des logiciels commercialisés
utilisent des images
icones (boutons, barre de titre, etc.), logos, aides, etc.
Deux types d’images
Image vectorielle
Composée d’objets géométriques (courbes définies par des formules
mathématiques comme des segments de droite, cercles, etc.)
Diverses extensions (.SVG, .DXF, .DWG, .SWF, .EPS)
Le zoom ne dégrade pas la qualité de l’image
Image matricielle (bitmap)
Matrice de points appelés pixels (PICture Element)
Diverses extensions (.bmp, .jpg, .gif, .png, etc.)
Le zoom dégrade la qualité de l’image
Plan
Brève introduction à GUI
Fenêtres et gestion dévénements
Classe BufferedImage (java.awt.image.BufferedImage)
Classe ColorModel (java.awt.image.ColorModel)
Classe Raster (java.awt.image.Raster)
API java2D
Images numériques sous forme matricielle (bitmap)
Codées en RGB (Red, Green, Blue), en binaire ou en grisé
création et enregistrement
modification
Filtres : transformation affine (zoomer), convolution (flouter,
déterminer les contours, etc.),
Accès et modification de pixels, etc.

Interface graphique utilisateur (GUI)


Graphical User Interface (GUI) ?
Façade du programme qui le lie avec l’extérieur
Constituée de composants graphiques
fenêtres (JFrame), panneaux (JPanel), boutons (JButton), menus
(JMenu), champs de saisie (JTextField), étiquettes (JLabel), cases
à cocher (JCheckBox), etc.
Donne l’impression de commander l’exécution du
programme
Chaque composant peut générer un ensemble d’événements
Le composant est la source de ces événements
A chaque événement (appartenant à une catégorie d’événements)
est associé un écouteur d’événement
Spécifier un comportement suite à la survenue de l’événement
o Réaction
Objets graphiques
Trois types
Conteneurs principaux
Destinés à contenir d’autres composants graphiques
Fenêtres (JFrame)
Ne peuvent pas être contenus dans d’autres composants graphiques
Conteneurs intermédiaires
Peuvent contenir et être contenus dans des composants graphiques
Panneaux (JPanel)
Composants atomiques (widgets)
Eléments de base devant être contenus dans des conteneurs
principaux ou des conteneurs intermédiaires
Ajout au conteneur en invoquant la méthode add de celui-ci

Une fenêtre principale


Classe JFrame ou sous-classe de celle-ci
Exemple
JFrame fen = new JFrame () ;
JFrame fen = new JFrame ("Titre de la fenetre") ;
Rendre visible la fenêtre
fen.setVisible (true) ; // false pour la masquer
Retailler et/ou définir la position de la fenêtre
fen.setSize (width, height) ;
fen.setBounds(xOrigin, yOrigin, width, height) ;
Changer le titre
fen.setTitle ("Ma première fenêtre") ;
Ajouter un composant graphique
Container container = fen.getContentPane () ;
container.add (new JButton ("OK")); container.add (new JPanel ());
Un panneau
Classe JPanel
Exemple
JFrame fen = new JFrame () ; // d’abord une fenêtre
JPanel panneau = new JPanel() ;
Ajouter le panneau à la fenêtre
fen.getContentPane ().add (panneau) ;
Changer la couleur de l’arrière plan du panneau
panneau.setBackground (Color.GREEN) ;
Ajouter un composant graphique au panneau
panneau.add (new JButton ("OK")) ;

Un exemple complet d’une interface graphique


simple
import java.awt.Color; import java.awt.Container;
import javax.swing.*;
public class FenetrePrincipale extends JFrame {
public FenetrePrincipale () {
super ("Ma première fenêtre") ; this.setBounds(0, 0, 400, 150) ;
JButton panneauBouton, fenetreBouton ;
//Ajouter un composant graphique
Container container = this.getContentPane () ;
container.add (fenetreBouton=new JButton ("Bouton Fenêtre"), "South");
JPanel panneau = new JPanel() ; container.add (panneau);
//this.getContentPane ().add (panneau) ;
//Changer la couleur de l’arrière plan du panneau
panneau.setBackground (Color.GREEN) ;
//Ajouter un composant graphique au panneau
panneau.add (panneauBouton = new JButton ("Panneau")) ;
}
public static void main(String[] args) {
FenetrePrincipale fen = new FenetrePrincipale () ;
//Rendre visible la fenêtre
fen.setVisible (true) ;
}
}
Illustration de l’écoute et la gestion d’un clic sur
un bouton

actionPerformed ()

ActionListenerImplementation
Bouton

Gestion des événements


Actions sur les boutons
import java.awt.* ; import javax.swing.*;
//nouvelles commandes import
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class FenetrePrincipale extends JFrame {
public FenetrePrincipale () {
//Idem que le slide N°= 8
ActionListenerImplementation actionLisImpl
= new ActionListenerImplementation () ;
fenetreBouton.addActionListener(actionLisImpl) ;
panneauBouton.addActionListener(actionLisImpl) ;
}
//Idem que le slide N°= 8
}
class ActionListenerImplementation implements ActionListener {
public void actionPerformed(ActionEvent ev) {
if (ev.getActionCommand().equals("Panneau"))
System.out.println ("Action sur le bouton \"Panneau\"") ;
if (ev.getActionCommand().equals("Bouton Fenêtre"))
System.out.println ("Action sur le bouton \"Bouton Fenêtre\"") ;
}
}
Barre de menus
Création d’une barre de menus
JMenuBar menuBar = new JMenuBar () ;
Ajout d’une barre de menus
fenetre.setJMenuBar (menuBar) ;
Ajout d’un menu
JMenu menu = new JMenu ("Couleurs") ;
menu.setMnemonic (‘C’) ;
menuBar.add (menu) ;
Ajout d’un sous-menu
JMenu sousMenu = new JMenu ("Autres Couleurs") ;
sousMenu.setMnemonic (‘A’) ;
menu.add (sousMenu) ;

Barre de menus (suite)


Ajout d’un item à un menu
JMenuItem cVerteItem = new JMenuItem ("Vert", ‘V’) ;
menu.add (cVerteItem) ;
Ajout d’un item à un sous-menu
JMenuItem cNoireItem = new JMenuItem ("Noir", ‘N’) ;
sousMenu.add (cNoireItem) ;
Gestion des événements
cVerteItem.addActionListener (new ItemLisImpl ()) ;
cNoireItem.addActionListener (new ItemLisImpl ()) ;
Ecouteur d’événements
Class ItemLisImpl implements ActionListener {..}
Illustration :
Barre de menus

JMenuBar

JMenu

MenuItem

Revenons à notre cours


Traitons les images
Travailler sur les images
API java2D
Deux classes à connaître
Image (java.awt.Image)
Classe abstraite ne permettant pas d’accéder aux pixels
BufferedImage (java.awt.image.BufferedImage)
Sous-classe de Image
Implémente toutes les méthodes abstraites de la classe Image

Image

extends

VolatileImage BufferedImage

La classe BufferedImage
Contient
Un ColorModel
Manière d’interpréter les couleurs
TYPE_INT_ARGB (alpha, rouge, vert, bleu)
TYPE_INT_RGB (rouge, vert, bleu)
TYPE_BYTE_GRAY (niveau de gris, codage sur un seul octet)
Un WritableRaster : un Raster autorisé en écriture
Un Raster est composé de
DataBuffer
o Données brutes dans un tableau
SampleModel
o Interprétation des données brutes
Lecture/écriture des fichiers image
Paquetage javax.imageio
ImageIO
Description du contenu de fichiers image, y compris des
métadonnées (date, heure, lieu, sensibilité ISO, usage du flash,
vitesse d’obturation, etc.)
ImageReader, ImageReadParam et ImageTypeSpecifier
Contrôle du processus de lecture d'image
ImageWriter et ImageWriteParam
Contrôle du processus d'écriture des images
ImageTranscoder
Transcodage entre formats
IIOException
Signaler les erreurs

Lire un fichier image en java


Méthode statique read de la classe ImageIO
Consultation de l’extension du fichier et du numéro
magique (premiers octets du fichier)
Recherche d’un lecteur approprié
Utilisation de la classe ImageReader
Si un transcodage est nécessaire
Utilisation d’un transcodeur (classe ImageTranscoder)
Exemple : formats JPG (compressé) et GIF (avec animations)
Possibilité d’installation de nouveaux lecteurs
Installation de fichiers JAR (plug-ins)
Exemple : pour lire les fichiers TIFF
Retourne un objet de la classe BufferedImage
Si aucun lecteur n’est trouvé, la méthode statique read
renvoie null
Exemple : lecture d’un fichier image
protected void ajouterImage(File fichierImage) {
// dessiner une image à l'ecran
try {
monImage = ImageIO.read(fichierImage);
} catch (IOException e) {
e.printStackTrace();
}
repaint();
}

// l’objet ‘fichierImage’ pourrait être obtenu par


// File fichierImage = new File ("mesImages/image.jpg");

Quelques formats d’images


Format Avantages Inconvénients

BMP Format très simple (format Fichiers de grande taille


matriciel)
GIF Supporte les animations et Ne pend en charge que 256 couleurs
gère les pixels transparents
PNG Compression sans perte. Ne supporte pas
Supportent les images en Les animations
couleurs réelles (codées
sur 24 bits)
JPG Idéal pour les images Compression avec perte.
photographiques N'est pas bon pour les images
contenant du texte et toute autre
image où les données sont critiques
Formats supportés en lecture
par java en standard
Méthode statique getReaderFormatNames() de la
classe ImageIO
import javax.imageio.ImageIO ;
public class FormatsSupportes {
public static void main(String[] args) {
afficheFormatsSupportesEnLecture () ;
}

public static
void afficheFormatsSupportesEnLecture () {
String names[]
= ImageIO.getReaderFormatNames();
for (int i = 0; i < names.length; ++i)
System.out.println(" fomat supporté "
+ "en lecture :" + names[i]);
}
}

Affichage d’une image BufferedImage


Affichage dans un panneau
Possibilité d’affichage dans une autre image
Redéfinition de la méthode paintComponent de la classe
Jpanel
Utilisation de la méthode drawImage
public class PanDessin extends JPanel {
BufferedImage monImage = null;
// ...
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(monImage != null)
g.drawImage(monImage, 0, 0, null);
}
}
Enregistrer une image en java
Méthode statique write de la classe ImageIO
Renseigner l’extension du fichier et le nom du fichier
Utilisation de la classe ImageWriter

protected void enregistrerImage(File fichierImage) {


String format ="JPG" ;
// Obtenir l’image à enregister et la mettre dans la
// référence image
BufferedImage image = getImagePanneau() ;
try {
ImageIO.write(image, format, fichierImage);
} catch (IOException e) { e.printStackTrace() ;}
}

Formats supportés en écriture


par java en standard
Méthode statique getWriterFormatNames() de la
classe ImageIO
import javax.imageio.ImageIO ;
public class FormatsSupportes {
public static void main(String[] args) {
afficheFormatsSupportesEnEcriture () ;
}

public static
void afficheFormatsSupportesEnEcriture () {
String names[]
= ImageIO.getWriterFormatNames();
for (int i = 0; i < names.length; ++i)
System.out.println(" fomat supporté "
+ "en écriture:" + names[i]);
}
}
Affichage d’une image BufferedImage
Affichage dans un panneau
Possibilité d’affichage dans une autre image
Redéfinition de la méthode paintComponent de la classe
JPanel
Dessiner l’image : méthode drawImage (im, x, y, obs) ;
im : objet Image (BufferedImage)
x, y : coordonnées du coin supérieur gauche de l’image à dessiner
Obs : observateur pour un chargement asynchrone (ici null)
public class PanDessin extends JPanel {
BufferedImage monImage = null;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(monImage != null) g.drawImage(monImage, 0, 0, null) ;
//Pour ajuster l’image à la taille du panneau, utilisez :
//g.drawImage(monImage, 0, 0, getWidth(), getHeight(), null);
} // ...
}

Création d’image
Peut-être contenue dans un panneau
Peut contenir d’autres formes graphiques (cercles,
rectangles, etc.)
protected BufferedImage createImage(){
int ImWidth = 600, imHeight = 400 ;
BufferedImage image = new BufferedImage (ImWidth, imHeight,
BufferedImage.TYPE_INT_RGB);
// on récupère le contexte graphique de la BufferedImage
Graphics2D g = image.createGraphics();
// la couleur de dessin est le bleu
g.setColor (Color.blue);
// on dessine un cercle rempli de rayon 100 dont le centre est
// (ImWidth/2 + rayon, imHeight/2 + rayon)
int rayon = 100 ;
g.fillOval (ImWidth/2-rayon, imHeight/2-rayon, 2*rayon, 2*rayon) ;
// on libère les ressources utilisées pour le contexte graphique
g.dispose();
return image;
}
Opérations sur les images
Interface BufferedImageOp implémentée par :
AffineTransformOp
Transformation affine (x’ = a * x + b * y + c, y’ = a’ * x + b’ * y + c’)
ColorConvertOp
Modification des couleurs des pixels
Tableau d’objects ColorSpace ou ICC_Profile
ConvolveOp
Convolution mathématique (multiplication par une matrice kernel)
LookupOp
RescaleOp
Modification des couleurs des pixels de l’image bufferisée
couleur’ = couleur * facteur + deplacement

Application d’une opération de filtre


sur une image

BufferedImageOp.filter(BufferedImage src, BufferedImage dest)

Image processing
operation

filter
source image destination image

BufferedImageOp
Changement de la taille de l’image
(methode 1)
Effectuer une transformation affine
Image source vers une image de destination
Changement d’échelle
AffineTransform trans = AffineTransform.getScaleInstance(x, y) ;
x et y correspondent aux coefficients de multiplication
Interpolation des pixels
int interpolation = AffineTransformOp.TYPE_BICUBIC ;
Interpolation bicubique
Autres interpolations
TYPE_BILINEAR, TYPE_NEAREST_NEIGHBOR
Application de l’opération de transformation affine
AffineTransformOp operationRetailler
= new AffineTransformOp (trans, interpolation) ;
operationRetailler.filter(monImage, imageRetaillee) ;

Illustration (méthode 1) : changement de la


taille d’une l’image
protected void zoomerImage (double coef)
{
BufferedImage imageRetaillee
= new BufferedImage ((int)(monImage.getWidth()*coef),
(int)(monImage.getHeight()*coef), monImage.getType());

AffineTransform zoom = AffineTransform.getScaleInstance (coef, coef);


int interpolation = AffineTransformOp.TYPE_BICUBIC ;
AffineTransformOp retaillerImage = new AffineTransformOp (zoom,
interpolation);
retaillerImage.filter(monImage, imageRetaillee );
monImage = imageRetaillee ; //change l’image à afficher dans mon panneau
repaint(); //réafficher le panneau
}
Changement de la taille de l’image
(methode 2)
Effectuer une transformation affine
Image source vers une image de destination
Changement d’échelle
AffineTransform zoom
= new AffineTransform (m00, m10, m01, m11, m02, m12) ;
Interpolation des pixels
Application de l’opération de transformation affine
AffineTransformOp operationRetailler
= new AffineTransformOp (trans, interpolation) ;
operationRetailler.filter(monImage, imageRetaillee) ;

Illustration (méthode 2) : changement de la


taille d’une l’image
protected void zoomerImage (double coef)
{
BufferedImage imageModifiee = new BufferedImage
((int)(monImage.getWidth()*coef), (int)(monImage.getHeight()*coef),
monImage.getType());
//new AffineTransform (double m00, double m10,
// double m01, double m11, double m02, double m12)
AffineTransform zoom = new AffineTransform (coef, 0., 0., coef, 0., 0.) ;

int interpolation = AffineTransformOp.TYPE_BICUBIC ;


AffineTransformOp retaillerImage = new AffineTransformOp (zoom,
interpolation);
retaillerImage.filter(monImage, imageModifiee);
monImage = imageModifiee ; //change l’image à afficher dans le panneau
repaint(); //réafficher le panneau
}
// Transformation affine
// [ x'] [ m00 m01 m02 ] [ x ] [ m00 . x + m01 . y + m02 ]
// [ y'] = [ m10 m11 m12 ] [ y ] = [ m10 . x + m11 . y + m12 ]
// [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
Récupération des pixels de l’image
et de leurs données
Objet Raster pour récupérer les données de l’image
Récupérer le tableau de pixels
Objet de ColorModel pour interpréter les données
TYPE_INT_ARGB, TYPE_INT_RGB, TYPE_BYTE_GRAY
int x = 30, y = 20 ;
Raster tramePixel = monImage.getRaster();
ColorModel modeleCouleur = monImage.getColorModel();

Object objCouleur = tramePixel.getDataElements(x, y, null) ;


//null pour allouer l’objet à retourner

// nombre et type de transfert


//tramePixel.getNumDataElements() ;
//tramePixel.getTransferType() ; // type du tableau retourné

System.out.println("teinte rouge = "+ modeleCouleur.getRed(objCouleur));


System.out.println("teinte verte = "+ modeleCouleur.getGreen(objCouleur)) ;
System.out.println("teinte bleu = "+ modeleCouleur.getBlue(objCouleur));

Modifier les données (couleur) d’un pixel


Objet Raster pour récupérer les données de l’image
Récupérer le tableau de pixels
Objet de ColorModel pour interpréter les données
TYPE_INT_ARGB, TYPE_INT_RGB, TYPE_BYTE_GRAY
int xPixel = 30 ;
int yPixel = 20 ;

WritableRaster trameModifiable = monImage.getRaster();


ColorModel modeleDeCouleur = monImage.getColorModel();

int rgb = Color.white.getRGB();


Object couleurBlanche = modeleDeCouleur.getDataElements(rgb, null);

// la pixel qui se trouve à la position (30,20) va prendre la couleur blanche


trameModifiable.setDataElements(xPixel, yPixel, couleurBlanche);
Modifier globalement les couleurs des pixels
RescaleOp
for each pixel from Source object {
for each band/component of the pixel {
dstElement = (srcElement*scaleFactor) + offset
}
}

protected void imageEclaircie() {


/*
* RescaleOp brillance = new RescaleOp(A, K, null); 1. A < 1, l'image
* devient plus sombre. 2. A > 1, l'image devient plus brillante. 3. K
* est compris entre -256 et 255 et ajoute un éclaircissement .
*/
BufferedImage imgBrillant = new BufferedImage(monImage.getWidth(),
monImage.getHeight(), BufferedImage.TYPE_INT_RGB);
RescaleOp brillance = new RescaleOp (1 / .7f, -30, null);
brillance.filter(monImage, imgBrillant);
monImage = imgBrillant;
repaint();
}

Transformation d’une image


RGV vers niveaux en gris
Création d’une nouvelle image en niveau de gris
Attribut BufferedImage.TYPE_USHORT_GRAY
Dessiner l’image RGB dans l’image en niveaux de gris
protected void imageEnNiveauGris() {
// monImage est une image RGB
BufferedImage imageGris = new BufferedImage(monImage.getWidth(),
monImage.getHeight(), BufferedImage.TYPE_USHORT_GRAY);
Graphics2D surfaceImg = imageGris.createGraphics();
surfaceImg.drawImage(monImage, null, null);
monImage = imageGris;
repaint() ;
}
Binarisation d’une image
Image binaire
Deux couleurs : noir (bit égal à 1) ou blanc (bit égal à 0)
Deux méthodes pour obtenir une image binaire
BufferedImage.TYPE_BYTE_BINARY
Transformation d’une image pixel par pixel
Image RGV vers image en niveau de gris
Méthode de seuillage pour passer d’une image en niveaux de gris
vers une image binaire
o Si la valeur du pixel est inférieure à un seuil alors
• Valeur pixel = 0
o Sinon
• Valeur pixel = 1
o Finsi

Transformation d’une image


Niveaux en gris vers binaire
Création d’une nouvelle image en binaire
Attribut BufferedImage.TYPE_BYTE_BINARY
Avec Java, il est possible de dessiner directement une
image RGB dans une image binaire

protected void imageBinaire() {


// monImage est une image RGB
BufferedImage imgBinaire = new BufferedImage(monImage.getWidth(),
monImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Graphics2D surfaceImg = imgBinaire.createGraphics();
surfaceImg.drawImage(monImage, null, null);
monImage = imgBinaire;
repaint();
}
Convolution mathématique
Appliquer un filtre à une image
Utilisation d’un masque (appelé aussi noyau)
Exemple de masque (pour flouter une image)

K=

Soit anm la donnée du pixel (n, m)


La donnée dnm du pixel (n, m) dans l’image de destination :
dn, m = ∑i = (0, 2) ∑j = (0, 2) (an-i+1, m-j+1 * Kij)
Détermination des contours par l’application de la
convolution

Khorizontal = Kvertical =

Appliquer la convolution
Choisir un masque et appliquer une opération de
convolution
protected void imageConvolue(float [] masqueConvolution, int nbLines) {
//on va utiliser le masque flou
BufferedImage imageFlou = new BufferedImage(monImage.getWidth(),
monImage.getHeight(), monImage.getType());

Kernel masque = new Kernel (nbLines, nbLines, masqueConvolution);

ConvolveOp operation = new ConvolveOp(masque);


operation.filter(monImage, imageFlou);
monImage = imageFlou;
System.out.println("convolution effectuée");
repaint();
}
Obtenir l’image affichée dans le panneau
Envoyer l’image affichée dans un panneau dans un
objet BufferedImage
Panneau.paintAll (contexteGraphiqueBufferedImage)
// A l’intérieur d’une sous-classe de JPanel
protected BufferedImage getImagePanneau() {
// récupérer une image du panneau
int width = this.getWidth();
int height = this.getHeight();

BufferedImage image = new BufferedImage(width, height,


BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();

this.paintAll(g);

g.dispose();
return image;
}

Conclusion
API java2D
Chargement et enregistrement des images
Affichage des images
Modification des images
Appliquer une convolution (filtres)
Agrandissement, réduction, détection de contours, floutage, etc.
Modification brute des pixels
D’autres API sont disponibles sous java pour traiter les
images
Java Advanced Image (JAI)
Etend l’API java2D
Compatible avec l’API java2D
java3D

Vous aimerez peut-être aussi