57
RAPPORT DE STAGE 2 ème année du cycle ingénieur ELEVE-INGENIEUR : Nom : ROUBAUD Prénom : Mickaël MAJEURE : Systèmes d’Information & Réseaux Systèmes Embarqués Télécommunications & Réseaux ENTREPRISE : Nom : Université Paris 5 – UFR Mathématiques et Informatique Adresse : 45 rue des Saints Pères 75006 PARIS Adresse du lieu de stage si différent : Confidentiel : oui non Signature du Maître de Stage : DESCRIPTION DE LA MISSION : A partir des travaux de thèse de Nicolas Loménie sur la vision par ordinateur, le stagiaire devra implémenter une applet Java de démonstration des outils développés. La vision stéréoscopique permet d'obtenir des nuages de points 3D qu'il faut ensuite soumettre à des traitements algorithmiques pour les interpréter. L'application est l'évitement d'obstacles en robotique mobile. (partie développement). Une fois l'algorithme porté en Java et exploité sous la forme d'une applet, le stagiaire proposera des améliorations aux traitements proposés (partie recherche). Parallèlement, une étude de la visualisation 3D de structures microscopiques sous le logiciel imageJ pourra être entreprise (partie R&D) dans le cadre d'un projet de recherche démarré avec un laboratoire de biologie cellulaire.

2ème année du cycle ingénieursteakhouse.free.fr/rds/rds_2003.pdf · RAPPORT DE STAGE 2ème année du cycle ingénieur ELEVE-INGENIEUR : Nom : ROUBAUD Prénom : Mickaël MAJEURE

  • Upload
    others

  • View
    6

  • Download
    0

Embed Size (px)

Citation preview

  • RAPPORT DE STAGE 2ème année du cycle ingénieur

    ELEVE-INGENIEUR : Nom : ROUBAUD Prénom : Mickaël

    MAJEURE :

    Systèmes d’Information & Réseaux Systèmes Embarqués Télécommunications & Réseaux

    ENTREPRISE : Nom : Université Paris 5 – UFR Mathématiques et Informatique Adresse : 45 rue des Saints Pères 75006 PARIS Adresse du lieu de stage si différent : Confidentiel : oui non Signature du Maître de Stage : DESCRIPTION DE LA MISSION :

    A partir des travaux de thèse de Nicolas Loménie sur la vision par ordinateur, le stagiaire devra implémenter une applet Java de démonstration des outils développés. La vision stéréoscopique permet d'obtenir des nuages de points 3D qu'il faut ensuite soumettre à des traitements algorithmiques pour les interpréter. L'application est l'évitement d'obstacles en robotique mobile. (partie développement).

    Une fois l'algorithme porté en Java et exploité sous la forme d'une applet, le stagiaire proposera des améliorations aux traitements proposés (partie recherche).

    Parallèlement, une étude de la visualisation 3D de structures microscopiques sous le logiciel imageJ

    pourra être entreprise (partie R&D) dans le cadre d'un projet de recherche démarré avec un laboratoire de biologie cellulaire.

  • Laboratoire SIP 45 rue des saints pères 75006 Paris

    Ecole Centrale d’Electronique 53 rue de Grenelle 75007 Paris

    responsable chercheur :Nicolas Loménie avril-août 2003

    Segmentation et visualisation « on-line » de nuages de points 3D

    Mickael ROUBAUD

  • - - 1 - -

    __________________________________________________Sommaire

    Le laboratoire « Systemes intelligents de Perception » Fiche d’identité 3 Activités du laboratoire 4 La mission 4 Cahier des charges La reconstruction 3d 5 Planning 6 Description du travail L’environnement de programmation 7 L’algorithme de partitionnement 8

    Recherche de la partition optimale 11 Premiers résultats 12 Visualisation 3d 13 Classes 15 Applet développée L’interface 16 Utilisation 19 Fichiers pris en charge 20 Aide java vm 21

    Benchmarks 22 Conclusion 23 Annexes Glossaire 25 Resultats 26 Sources 28 Code 29

    Les mots en italique suivis de * sont définis dans le glossaire, p.25

  • - - 2 - -

    Je tiens tout d’abord à remercier : Mon tuteur de stage, Nicolas Loménie qui a été très présent durant ce stage. Frédéric Ravaut, qui m’a mis en contact avec l’équipe du SIP. Merci aussi à Jérôme pour sa sympathie et enfin à Georges Stamon, directeur du laboratoire, qui a bien voulu m’accueillir. A Laure, pour l’amour qu’elle me donne tous les jours…

  • - - 3 - -

    ___________________________________________Le laboratoire SIP Fiche d’identité : Le laboratoire est situé au sein de l’université René Descartes, UFR Paris 5 –

    Laboratoire CRIP5- Equipe SIP

    UFR Mathématiques & Informatique Université Paris 5

    45 rue des Saints Pères

    75006 PARIS

    tél.:(33) 01.44-55-35-56

    fax: (33) 01.44-55-35-36

    email : [email protected]

    http://www.math-info.univ-paris5.fr/sip-lab/

    L’université Paris 5 L’entrée Le laboratoire en lui même est situé au 7ème étage dans le pavillon Sappey.

    Mon poste de travail

  • - - 4 - -

    Activités du laboratoire (extrait du site web) : L'équipe " Systèmes Intelligents de Perception ", SIP, (UFR " Mathématiques et Informatique " - Université René Descartes) a été créée en janvier 1990. Equipe d'accueil doctoral, attachée au DEA " Intelligence

    Artificielle, Reconnaissance des formes et Applications ", IARFA, (Paris 6, Paris 5, ENPC, ENSTA, ENST), elle regroupe des enseignants de l'Université et une dizaine de thésards, essentiellement boursiers du Ministère de l'Education Nationale et CIFRE. Depuis 1998, l'équipe fait partie du Centre de Recherche en Informatique de Paris 5 (CRIP-5). L'action thématique de l'équipe est orientée dans le domaine de la Perception Visuelle, accompagnée de quelques éléments d'Intelligence Artificielle (filtrage sémantique et l'utilisation du contexte, contrôle intelligent, interprétation,...). L'orientation scientifique de l'équipe et la phrase-clé " Analyse et Interprétation d'Images " sont illustrées par plusieurs études de modélisation, de méthodologie et d'applications. Ces quelques considérations expliquent à la fois la structure et l'évolution du laboratoire. Pendant la période 1990-2003, vingt cinq thèses ont été préparées et soutenues. Les vingt cinq docteurs ont intégré la vie professionnelle et assument actuellement des responsabilités correspondant à la compétence scientifique acquise pendant leur formation doctorale au laboratoire CRIP5-SIP. Pendant la même période 1990-2003, plus de cent papiers (congrès internationaux et nationaux, colloques, revues) ont été présentés. L'équipe a accueilli une dizaine de visiteurs universitaires (durée de séjour scientifique entre une semaine et un mois) et deux post-doctorants. Des séminaires mensuels sont également organisés. Le travail du laboratoire CRIP5-SIP est largement ouvert vers l'extérieur. Un certain nombre d'experts " extérieurs " participent activement à la formation doctorale comme des professeurs ou des docteurs venant du monde entier. Actuellement, le laboratoire a passé des contrats avec le Cnes, l’IGN, plusieurs universités et même IBM Californie. Les domaines de recherche se concentrent autour de l’analyse et l’interprétation d’images, tout en étant divers : -Imagerie bio-médicale -Reconstruction 3D -Analyse, codage d'images et indexation -Analyse de documents -Reconnaissance de formes

    _________________________________________________La mission Suite à une rencontre avec Frédéric Ravaut dans le cadre de la mineure recherche à l’ECE, nous avons été amenés à discuter d’un éventuel stage dans le laboratoire SIP auquel il est associé. J’ai pu me rendre au labo pour parler avec des chercheurs, découvrir leurs sujets de recherche et leurs besoins. Je décidais de travailler avec Nicolas Loménie sur la reconstruction 3D*, domaine passionnant, que j’avais envie d’appronfondir. Il avait besoin d’un logiciel expliquant sa démarche dans la reconstruction 3D et montrant les résultats obtenus. En effet, Nicolas désirait développer une applet Java pour pouvoir faire une démonstration en ligne ; de mon côté je programmais déjà depuis quelques années en ce langage. La partie R&D sous ImageJ a finalement été traitée par Laurianne Gresser, autre stagiaire du SIP.

  • - - 5 - -

    __________________________________________________________Cahier des charges La reconstruction 3D : Dans le cadre de la robotique mobile et de la vision par ordinateur, la stéréoscopie* est le moyen le plus économique et le plus utilisé pour pouvoir obtenir une vue en 3D de l’environnement. C’est dans ce contexte que Nicolas Loménie a présenté en 2001 sa thèse « Interprétation de nuages de points : application à la modélisation d’environnements 3D en robotique mobile ». Y est décrit comment, en partant de nuages de points issus de systèmes d’acquisition 3D (stéréovision ou autre) il est possible de modéliser l’environnement observé et de le reconstruire en 3D. Les nuages de points peuvent être assimilés à des images bitmap, à cette particularité près : les points ne sont pas répartis de façon homogène sur toute la surface (ou le volume). Il faut donc connaître la position de chaque point pour pouvoir effectuer un quelconque traitement ce qui demande plus de mémoire. Plusieurs étapes sont nécessaires avant d’arriver à une modélisation en objets 3D à partir d’une vue stéréoscopique :

    - appariement* des images stéréoscopiques pour créer un nuage de points* 3D - pré-traitements : par exemple re-échantillonnage pour réduire le nombre de points - regroupement de points - reconstruction des objets par alpha formes

    Image vidéo d’origine Nuage de points 3D extrait et partitionné*

    Nuage de points avec objet reconstruit Objet reconstruit

    Je me suis penché sur la partie regroupement de points. Lors de sa thèse, les tests des solutions proposées avaient été effectués avec des logiciels développés en C++ sous Linux –sans aucune interface graphique- et à l’aide de programmes spécialisés en traitement d’image.

  • - - 6 - -

    Nicolas avait besoin d’une visualisation montrant le fonctionnement de son algorithme de regroupement, basé sur les C moyennes floues (Fuzzy K-means) [Gath Geva 1989] et sur l’utilisation d’une distance particulière, appelée « distance exponentielle ». Dès le départ, le choix s’est porté sur Java, langage qui permet de faire fonctionner des applets au sein de pages web et de faire une démonstration en ligne. Le cahier des charges a évolué au fur et à mesure du stage, pour finalement aboutir sur ces fonctionnalités:

    -chargement de fichiers nuage de points 3D -partitionnement suivant les algorithmes définis par Nicolas Loménie dans ses travaux de thèse -visualisation 3D du nuage partitionné -possibilité de créer son propre nuage en ligne -l’interface, définie assez tard dans le stage doit se présenter ainsi :

    Planning: Le planning lui aussi a évolué tout au long du stage, mais les grandes lignes été respectées. Voici le planning tel qu’il a été initialement prévu et ce qui a finalement été effectué :

    DATE PREVISION EFFECTUE 14 avril début du stage début du stage 17 avril 1ère application graphique en java 22 avril 1ère application java3D 25 avril routine de segmentation 2D validée

    10 mai routine de détection de l’optimal validée, nom des fonctions, 1ère javadoc, découpage en classes

    15 mai maîtrise java, segmentation commencée 1ère version de la segmentation

    améliorée

    21 mai choix définitif de Java3D pour la visualisation

    23 mai intégration de java3D à l’application de segmentation 28 mai 1ère version applet 26 juin kmeans 2D améliorée validée 30 juin kmeans 2D validée kmeans 3D améliorée validée

    21 juillet nouvelle disposition de l’interface 15 août kmeans 3D validée 24 août fin du stage

    30 septembre remise du rapport remise du rapport

    Moteur d’interférence

    Visualisation

    Connaissances

    Paramètres de visualisation

    Chargement fichier

    Moteur d’interférence

    Visualisation

    Connaissances

    Paramètres de visualisation

    Chargement fichier

  • - - 7 - -

    _______________________________________________________Description du travail Mon travail s’est divisé en tâches ; compréhension des méthodes de partitionnement*, recherche de documentation sur la visualisation 3D en java et enfin programmation des algorithmes de partitionnement. J’ai tout d’abord du analyser les travaux de Nicolas afin de comprendre les algorithmes utilisés. J’ai commencé par lire grossièrement sa thèse pour y trouver les principes de la vision stéréoscopique et plus précisément du partitionnement de nuage de points. En même temps, il m’a fallu trouver des solutions au cahier des charges car la définition n’était pas très précise et j’ai ainsi du comparer les différents moyens de satisfaire les besoins du laboratoire. L’environnement de programmation Il fallait tout d’abord définir l’outil avec lequel j’allais développer. Ceci m’a demandé un travail de documentation et de recherche sur Internet. Quelques jours après l’environnement de programmation Borland Jbuilder sous Windows, déjà utilisé au labo et disponible en licence « personal » gratuite était adopté.

    L’environnement Jbuilder Jbuilder est un environnement agréable à utiliser, contenant un correcteur syntaxique. Tout est inclus pour le développement Java, de l’éditeur à la Machine Virtuelle. On dispose aussi d’un outil de conception graphique qui permet de placer les composants de l’application ou applet. J’ai donc commencé à me remettre à la programmation Java dès la première semaine de stage en constituant de petites applications graphiques.

  • - - 8 - -

    L’algorithme de partitionnement Parallèlement j’effectuais une lecture approfondie de l’algorithme de partitionnement et notamment du papier de Gath et Geva paru en 1989 « Unsupervised Optimal Fuzzy Clustering ». Cet algorithme est basé sur Fuzzy K means et permet de partitionner un nuage de points, c’est à dire de regrouper des points en « amas ». Dans les années 1980-1990 ce type d’algorithme est utilisé en statistique et sera introduit plus tard dans le domaine de la vision 3D. Le principal intérêt de cette méthode est d’être non supervisée, c’est à dire sans intervention humaine, et de pouvoir partitionner des nuages qui sont loin d’être structurés. Dans la vision par ordinateur ce type d’algorithme permet de découper un environnement en objets et donc de pouvoir décrire une scène en terme d’objets ; par exemple à telle position un arbre, ici un rocher, etc. L’application qui nous intéresse est la robotique mobile, et donc la détection d’obstacles. Le principe de l’algorithme de base est le suivant: Soit Vi le centroide du ième cluster et Xj le jème site ; K le nombre de clusters de la partition ; N le nombre de sites du nuage.

    [1] choisir les centroides des clusters* (par exemple de façon aléatoire ; mais dans notre application nous avons initialisés ces centroides au point 0)

    [2] calculer le degré d’appartenance de chaque site à chaque cluster à partir de d², la distance entre un site j et le centroide d’un cluster i :

    (dans ce cas la matrice A est un matrice identité

    et on a donc une distance euclidienne) [3] calculer les nouveaux centroides

    [4] mettre à jour les degrés d’appartenance des sites -si le changement maximal des degrés d’appartenance est inférieur à une valeur fixe,

    on arrête l’algorithme, sinon on retourne en 3

    ( ûij est le degré d’appartenance actualisé, uij le calcul de l’itération précédente)

    Ainsi on calcule les degrés d’appartenance de tous les sites à tous les clusters, et aussi les centroides de ceux ci. Ce degré d’appartenance est une valeur entre 0 et 1 :

    - si égal à 0 alors le site n’appartient pas à ce cluster - si égal à 0.5, il se peut que le site appartienne au cluster, mais on n’en est pas certain - si égal à 1 alors le site appartient sûrement au cluster

    Les résultats sont, à ce stade, encore flous ; il faudra donc effectuer une « défuzzyfication* » afin de pouvoir dire clairement et de façon bien déterminée si tel site appartient à tel cluster ou à tel autre. Nous avons effectué un simple seuillage des uij, à 0.5.

  • - - 9 - -

    Pour mieux comprendre le fonctionnement de cet algorithme j’ai été amené à développer une première application permettant le partitionnement de nuage de points 2D :

    Nuage de points Evolution des centroides L’algorithme de base est implémenté. On voit à gauche le nuage de points (sites définis aléatoirement) partitionné en 5. Chaque cluster a une couleur différente et les sites lui appartenant sont reliés au centroide. A droite, une représentation de l’évolution des positions des centroides au cours de l’algorithme. On voit que même positionnés au départ aléatoirement sur la surface du nuage de points, chaque centroide finit par converger vers une position bien définie. La convergence des centroides est beaucoup plus rapide quand on applique l’algorithme sur un nuage un peu plus structuré :

    Nuage de points Evolution des centroides Il faut moins d’une dizaine d’itérations à l’algorithme pour converger et partitionner parfaitement le nuage en 2 (ajuster les positions des centroides et calculer les uij). Une façon d’améliorer l’efficacité de l’algorithme, notamment en cas de partitions proches et de densités différentes est d’introduire une distance exponentielle à la place de la distance euclidienne.

    (h(i|Xj) est l’équivalent du degré d’appartenance et nous l’utiliserons en tant que tel ; Fi est la matrice de covariance floue du ième cluster)

  • - - 10 - -

    2-partition avec distance euclidienne 2-partition avec distance exponentielle On voit que la différence est flagrante et que cette amélioration de l’algorithme partitionne beaucoup mieux ce type de nuage. Malheureusement, c’est au prix d’un temps de calcul beaucoup plus important. Devant tester cet algorithme et n’ayant pas de base de nuages, j’ai du confectionner des nuages de points 2D (l’algo a été codé en 2D dans un premier temps). Une façon simple de créer ces nuages était de les dessiner sous Paint, de les enregistrer en BMP 8 bpp, puis de les charger avec l’application Java. Il suffit alors de transformer l’image bitmap en nuage de points 2D. Le principe est de balayer l’image, à la recherche des points noirs par exemples.

    La création de nuages 2D sous Paint. J’ai ensuite pu me procurer auprès de Nicolas Loménie des nuages 2D de test et des nuages 3D extraits des travaux sur le projet LAMA.

  • - - 11 - -

    Recherche de la partition optimale L’algorithme permet aussi la recherche du nombre de clusters optimal : Pour cela, on partitionne entre un Kmin et un Kmax ; à chaque partition on calcule un critère de validité de partition. Si on considère ce critère comme valide, alors on renvoi la (K-1)partition.

    K-partitionne

    K=K+1 K

  • - - 12 - -

    Premiers résultats

    -10

    0

    10

    20

    30

    40

    50

    1 2 3 4 5 6

    DpaDérivée

    Un nuage 2D partitionné (fichier ligcer.data de 2000 points) avec l’algorithme utilisant la distance exponentielle. L’algorithme trouve bien que la dérivée décroît pour K=4 et donc que la 3-partition est optimale. (On considère toujours qu’il y a au moins 2 clusters dans le nuage)

    Nuage 3D de 2063 points (fichier 3d.bmp). Là aussi l’algorithme obtient le résultat correct, à savoir une 3-partition.

    L’algorithme trouve que la 2-partition est optimale (fichier 3d2.bmp de 1472 points).

    0

    2

    4

    6

    8

    10

    12

    0 1 2 3 4 5 6 7 8 9

    dpadérivée

    -5

    0

    5

    10

    15

    20

    0 1 2 3 4 5 6 7 8 9

    dpadérivée

  • - - 13 - -

    Visualisation 3D Au niveau de la visualisation des nuages de points 3D, deux solutions se présentaient:

    - soit développer un moteur 3D très simple n’affichant que des points - soit utiliser l’api Java3D

    L’avantage de la première solution était de pouvoir utiliser une méthode simple et légère. Mais en devant se limiter à l’affichage de points. La solution Java3D présente l’avantage d’être très puissante (affichage d’objets complexes, gestion de la lumière, des textures…) mais au prix d’une certaine lourdeur et d’une programmation spécifique. J’ai finalement opté pour cette dernière api en se rendant compte que l’affichage de lignes ou d’objets complexes profiterait à la visualisation des nuages. Il a donc fallu installer l’api Java3D aussi bien pour la Machine Virtuelle que pour Jbuilder. Ensuite j’ai du apprendre à programmer avec cette api, et a comprendre comment elle fonctionnait. Java3D est un ensemble de bibliothèques Java permettant de visualiser un environnement 3D composé d’objets complexes. La programmation débute par la création d’un arbre scénique :

    exemple de graphe scénique

    Nous n’avons en fait utilisé qu’une petite partie des possibilités de Java3D, notamment en partant d’un monde 3D prédéfini qui nous évite une bonne partie du paramétrage (tout ce qui concerne le physique de l’environnement et d’utilisation ainsi que la plate-forme d’utilisation). En utilisant cet univers minimal il ne nous reste plus qu’à créer nos objets.

    BG

    S

    BG

    TG

    Apparence Géométrie

    S

    Apparence Géométrie

    BG

    S

    TG

    VirtualUniverse (univers virtuel)

    Locale (scène)

    BranchGroup (groupe de branchement)

    TransformGroup (groupe de transformation)

    Shape (objet)

    NodeComponent

    Relation parent-enfant

    référence

  • - - 14 - -

    J’ai donc du créer un arbre scénique pour créer l’environnement de visualisation de nuages de points 3D : -tr est un TransformGroup associé à un comportement permettant de faire tourner, zoomer et déplacer l’ensemble du nuage de points à la souris -circle est le nuage des points à proprement parler (sous forme de tableau de points Java3D :PointArray) -box est la boîte contenant le nuage de points 3D définie comme un tableau de ligne (LineStripArray) mais qui n’est créée et rattachée que si l’option est cochée dans l’interface graphique -centre est un objet 3D (en fait une sphère) définie pour chaque centroide du nuage et placée à la bonne position grâce à tr_c un TransformGroup. Pour chaque centroide on défini un ensemble centre-tr_c.

    S Comportement

    simpleU

    scene

    TGtr

    BG

    myMouseRotate,myMouseTranslate, myMouseZoom

    circle

    Scentre

    TG tr_c

    Scentre

    TG tr_c Sbox

    Un problème est apparu, pour mettre à jour l’affichage lors de changement de paramètres de visualisation (affichage ou non de la box ou des centroides par exemple). La solution qui vient en premier à l’esprit est de détruire cet arbre et d’en reconstruire un autre avec les options d’affichage. Mais pour des raisons de temps de calcul long, une alternative a du être trouvée; après plusieurs tests j’ai abouti à cette solution : - détacher le branchgroup scene - effacer les enfants du TransformGroup tr - recréer les enfants de tr en prenant en compte les nouveaux paramètres - rattacher le branchgroup au SimpleUniverse Cette méthode permet de garder le même point de vue car on ne touche pas à tr ni au comportement qui lui est affecté. Nous sommes obligés de détacher l’arbre du SimpleUniverse afin de détruire les enfants du TransformGroup.

  • - - 15 - -

    Le comportement affecté à tr est très simple et géré par Java3D ; myMouseRotate permet de faire tourner l’objet à la souris, myMouseTranslate de le déplacer et myMouseZoom de le rapprocher ou de l’éloigner. L’application utilise aussi cette méthode pour le chargement des fichiers de nuages de points 3D et pour le partitionnement. Classes : Toutes ces parties du logiciel sont très différentes les unes des autres, et nous avons donc défini des classes* afin de pouvoir les réutiliser dans d’autres applications. Il a aussi fallu les rendre les plus génériques possible et éviter toute dépendance entre classes.

    Barre.java barre de boutons (moteur d’interférence) Cadre1.java gestion de l’interface graphique (affichage) Constants.java définition des constantes spécifiques à l’application Matrix.java calcul sur les matrices Nuage.java chargement et segmentation de nuages de points 2D et 3D

    View3d.java affichage de nuages de points 2D et 3D

  • - - 16 - -

    _______________________________________________________L’applet développée L’interface :

    Le moteur d’interférence : Permet de configurer le partitionnement du nuage.

    Type d’algorithme utilisé Nombre de clusters au départ Nombre maximal de clusters Continuer au delà de la recherche de l’optimal Niveau du seuil de défuzzyfication Lancement du partitionnement Choix du fichier Nuage de points à charger

  • - - 17 - -

    Les paramètres de visualisation : Configuration de la visualisation 3D.

    Affichage des centroides des clusters Remise à zéro du point de vue Affichage des sites affectés à aucun des clusters Blocage de l’axe X de la souris Affichage de la boite contenant le nuage Blocage de l’axe Y de la souris Taille des points affichés Couleur du fond de la visualisation 3D

    La visualisation 3D : C’est dans cette partie qu’est affiché le nuage de points chargé.

    Il est possible de changer le point de vue grâce à a souris : Déplacement de la souris avec clic gauche permettent d’effectuer des rotations suivant les axes X Y et Z (uniquement pour les nuages 3D). Déplacement de la souris avec clic droit permettent d’effectuer des déplacements suivant les axes X et Y. Déplacement de la souris, avec clic gauche et touche ALT enfoncée permettent d’effectuer un déplacement suivant l’axe Z. Les différents clusters sont identifiés par des couleurs et leur centroide est représenté par une sphère.

  • - - 18 - -

    Barre d’information: Affiche des informations sur le nuage et le partitionnement.

    Type de nuage (3D ou 2D) Nombre de clusters du nuage Nombre de points total du nuage Nombre de points non affectés, c’est à dire qui n’appartiennent à aucun des clusters

    L’affichage de l’image d’origine : Affiche une des 2 images de la prise de vue stéréoscopique ayant servi à la création du nuage de points 3D. L’image affichée est le nom du nuage suivi de « .jpg ». Ex : image3d.2.016.xyz.jpg sera affichée si le nuage image3d.2.016.xyz est chargé.

    L’image est mise à la taille mais avec un ratio W/H constant. Si l’image n’est pas trouvée, le logo SIP est affiché. La console de debbogage : Les informations relatives au partitionnement et à la recherche de l’optimal sont affichées ici.

    Entre autres la position dans l’espace des centroides et les valeurs du DPA et de sa dérivée.

  • - - 19 - -

    Utilisation : Utilisation normale en ligne : o Depuis la home page, cliquer sur l'image pour lancer l'applet. Une fois l'applet apparue, choisir le nom du fichier à partitionner dans la liste déroulante. (étape absente si vous avec uploadé votre fichier nuage de points) o Le fichier est alors chargé et partitionné suivant l'algorithme de base et en K minimum partition (K égal à "de K=..."). o Choisir l'algorithme à utiliser pour partitionner le nuage. Choisir le nombre de clusters voulus, ou bien les limites inférieure et supérieure de K. o Cliquer sur le bouton GO. Le partitionnement est lancé et peut durer plusieurs minutes. Si les limites inférieure et supérieure sont différentes on partitionne le nuage pour toutes les valeurs de K entre ces limites; si la dérivée du critère DPA diminue le partitionnement cesse et la partition calculée avant est renvoyée. o Un clic sur le bouton "Continuer (k++)" permet de continuer au delà. o Les clusters sont différenciés par des couleurs différentes (il y a 8 couleurs différentes, qui se répètent). Les points gris sont les points qui ne sont affectés à aucun des clusters (points frontière). o La défuzzyfication est effectuée lors de l'affichage, suivant le seuil (S) défini dans l'interface. o Vous pouvez observer le nuage sous tous ses angles grâce à la souris. Un déplacement avec clic gauche permet de le faire tourner (fonctionnalité désactivée pour les nuages 2D). Un déplacement avec clic droit permet de déplacer le nuage dans le plan. Déplacement avec ALT et clic gauche permet de zoomer le nuage. Utilisation depuis une autre page HTML : Il est possible d'insérer l'applet dans n'importe quelle page HTML grâce au code suivant: -Le paramètre nom permet de passer une liste de fichiers nuage de points à charger séparés par une virgule. Ils seront affichés dans la liste déroulante de l'applet. Les noms sont en adresse relative par rapport à l'applet. L'applet n'a pas le droit de charger des fichiers présents à l'extérieur de son répertoire. -Un autre paramètre, upload permet de passer l'adresse d'un fichier uploadé sur le serveur à l'applet. Si ce paramètre est défini, il a la priorité sur nom; la liste déroulante de fichiers ne sera pas affichée et le fichier passé dans upload sera chargé automatiquement.

  • - - 20 - -

    Le choix du loader est déterminé par l'extension du fichier! .bmp (Bitmap 320x200x8bpp), .xyz (nuage 3D) ou bien .data (nuage 2D). Biensûr, il est possible de choisir la largeur (width) et la hauteur (height) de l'applet dans la page (en % ou en px). Attention, il faut inclure TOUTES les classes dans le même répertoire que la page html! Fichiers pris en charge : o BMP 320x200x8bpp:Fichier bmp 8 Bits Per Pixel Windows non compressés 320x200 en 256 couleurs; sont considérés comme points les pixels dont l'index de palette est inférieur à 250. Très pratique pour créer des nuages avec Paint. o DATA:Fichier nuage de points 2D sans entête; un point par ligne, X et Y séparés par un espace: 4.900 -9.980 4.908 -6.467 o XYZ:Fichier nuage de points 3D sans entête; un point par ligne, X Y et Z séparés par un espace: 40.397057 -10.983075 14.714388 36.064999 -10.079031 13.136463

  • - - 21 - -

    Aide Java VM: Allez dans le panneau de configuration (menu démarrer->paramètres->panneau de configuration). Double cliquez sur l’icône « Java Plugin »

    Cliquez sur l’onglet "Navigateur".

    Il faut s’assurer que le navigateur que vous utilisez est coché. La Java Virtual Machine du navigateur va alors être remplacée par celle que vous avez installé. Ensuite dans l’onglet "Propriétés Avancées", vous pouvez choisir la VM que vous voulez utiliser. Vérifiez que le plugin choisi soit bien le dernier installé et que c’est bien celui-ci que vous avez choisi lors de l’installation de Java3D.

  • - - 22 - -

    Si après avoir configuré correctement le plugin l'applet ne fonctionne toujours pas, désinstaller toutes les VM Java et Java3D de votre machine. Il est même possible de devoir réinstaller DirectX. Reprendre calmement et dans l'ordre les étapes de l'installation:

    -installer DirectX -installer Java2 -installer Java3D

    Benchmarks : Quelques temps de calcul de partition sur un PII333 (948 MIPS) sous MSIE 5.0 et Java 1.4.

    distance nom de à partition optimale temps (s) points nuage Euc 3d.bmp 2 à 8 3 (ok) 02 2063

    Exp 3d.bmp 3 3 06 2063

    Euc 3d2.bmp 2 à 8 2 (ok) 01 1472 Exp 3d2.bmp 2 à 8 3 09 1472

    Euc cercle.data 2 à 8 2 (ok) 01 1000

    Exp cercle.data 2 à 8 2 (ok) 06 1000

    Euc ligcer.data 2 à 8 2 01 2000

    Exp ligcer.data 3 3 25 2000

    On le voit, le temps de calcul dépend surtout de la complexité du nuage et pas simplement du nombre de points. Il faut aussi noter que le programme est ici en Java et qu’un portage en C permettrait un gain de vitesse jusqu’à 10. De plus les parties critiques ne sont pas du tout optimisées pour la vitesse. Un portage sur un robot mobile autonome qui reconstruirait l’environnement 3D en temps réel peut-être envisageable mais avec un puissance de calcul vraiment très importante (DSP ou PC embarqué).

  • - - 23 - -

    ______________________________________________________Conclusion Ce stage m’a beaucoup apporté aussi bien sur le plan personnel que sur le plan technique. Tout d’abord j’ai rencontré des chercheurs très actifs et présents dans le monde de la recherche en « vision ». Ceci m’a permis d’être en contact direct avec la recherche. L’ambiance du laboratoire était très sympathique, des réunions mensuelles permettant de tenir tous les membres au courant des travaux de chacun et de l’évolution du labo (contrats en vue par exemple). J’ai vraiment été immergé pendant 5 mois au cœur du labo, entouré de doctorants et de chercheurs. J’ai apprécié la confiance et l’autonomie qui m’ont été données. J’ai aussi appris à m’organiser pour réaliser un projet de moyenne envergure et dont le cahier des charges n’était pas totalement ni clairement défini ; j’ai donc du prendre des décisions, notamment en ce qui concerne l’interface graphique, pour rendre l’applet la plus conviviale possible. En même temps, Nicolas m’imposait des contraintes, et me guidait dans le développement. J’ai essayé de décomposer le travail en tâches, ce qui m’a permis de varier souvent le travail et de ne pas rester bloqué un mois sur une partie spécifique du programme. La création de l’IHM a justement été assez délicate ; en effet un des atouts de Java est son interface graphique. Mais c’est aussi un inconvénient car il existe alors des dizaines de méthodes , de librairies ou de fonctions pour placer des objets graphiques comme de simples boutons. J’ai donc parfois passé des heures pour placer simplement des composants graphiques dans l’interface. J’ai aussi rencontré des problèmes pour programmer correctement les équations des algorithmes qui sont loin d’être simples ; cette tâche n’a pas été évidente. Dans ce type de programmation, il faut surtout privilégier la réflexion et ne pas se précipiter. Le code source de l’application a été commenté au mieux, afin de permettre une évolution possible. Ce stage m’a vraiment intéressé et j’aurais voulu continuer mes études dans le monde de la recherche. Malheureusement il m’est actuellement impossible de postuler pour un DEA mais je garde l’espoir de présenter une thèse après quelques années d’expérience dans une entreprise et finalement rejoindre un jour le monde des chercheurs.

  • - - 24 - -

    ANNEXES

  • - - 25 - -

    ___________________________________________________Glossaire Appariement : mise en relation des pixels de l’image droite avec ceux de l’image gauche sur vue stéréoscopique. Cette phase permet d’extraire une carte de disparités et donc de l’environnement sous forme d’un nuage de points 3D. Applet : programme Java exécuté par le client, dans une page Web Centroïde : centre de gravité d’un cluster Classe : en langage objet, partie d’un programme réutilisable pour effectuer une fonction précise avec d’autres variables Cluster : amas de points, issu d’une partition Défuzyfication : fait de rendre logique un résultat flou par exemple effectuant un seuillage Fuzzy : flou Fuzzy K-means : (ou C moyennes floues) méthode de segmentation de nuages de points Nuage de points : ensemble de points pouvant être assimilé à une image mais sans répartition régulière ; la position de chaque point doit être connue Partition : nuage de point « découpé » en amas de points ; segmentation Partitionner : faire une partition ; synonymes clustering ou segmentater. Reconstruction 3D : reconstruction numérique spaciale de l’environnement ou d’un objet réel Stéréoscopie : système de 2 caméras couplées imitant la vision humaine Segmentation : (voir partition) Site : point appartenant à un nuage de points Uploader : de l’anglais to upload, envoyer vers un serveur

  • - - 26 - -

    _________________________________________________________________Résultats Comparatif : partitionnement avec distance exponentielle contre partitionnement avec distance euclidienne.

    distance exponentielle distance euclidienne

  • - - 27 - -

  • - - 28 - -

    _________________________________________________________Sources .Structuration plane d’un nuage de points 3D non structuré et détection des zones d’obstacle Loménie, Gallo, Cambou, Stamon – 1999 .Interprétation de nuages de points : application à la modélisation d’environnements 3D en robotique mobile Loménie – 2001 .Unsupervised Optimal Fuzzy Clustering Gath, Geva – 1989 .Initiation à l’API Java 3D Bouvier, K Computing traduction Fortun Armel – 1999 .GUI : Construire une interface graphique en java Genoud – 2001

  • - - 29 - -

    _______________________________________________________Code source Java View3d.java import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.*; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.*; import com.sun.j3d.utils.universe.*; import com.sun.j3d.utils.behaviors.mouse.*; import javax.media.j3d.*; import javax.vecmath.*; /** * Classe permettant l'affichage de nuages de points 2d ou 3d. * @author Mickael ROUBAUD - SIP, université René Descartes PARIS * @version V3d.3 - juin 2003 */ public class View3d extends Shape3D implements constants{ private SimpleUniverse simpleU; private BranchGroup scene; private Canvas3D canvas3D; private TransformGroup tr; private int properties[]; private MouseRotate myMouseRotate; private MouseTranslate myMouseTranslate; private MouseZoom myMouseZoom; private Background bg; /** * Constructeur.Génère un univers virtuel, un BG et un TG. * Les mouvements de la souris sont affectés au TG. * définit aussi les propriétés par défaut de la visualisation. */ public View3d(Cadre1 cadre) { properties= new int[10]; //cadre.getContentPane(). cadre.setLayout(new BorderLayout()); canvas3D =new Canvas3D(SimpleUniverse.getPreferredConfiguration()); //cadre.getContentPane(). cadre.add("Center",canvas3D); scene = new BranchGroup(); scene.setCapability(scene.ALLOW_CHILDREN_READ); scene.setCapability(scene.ALLOW_CHILDREN_WRITE); scene.setCapability(scene.ALLOW_DETACH); simpleU = new SimpleUniverse(canvas3D); simpleU.getViewingPlatform().setNominalViewingTransform(); set_properties(BG_COLOR,0); set_properties(CENTROIDS,true); //afficher les centroides? set_properties(UNCLASSIFIED,true); //afficher les points non affectés? set_properties(BOX,true); //affichage box set_properties(POINTS_SIZE,2); set_properties(BG_COLOR,0); set_properties(THRESHOLD,5); tr=new TransformGroup(); //création tranformgrooup this.reset(); //raz du point de vue scene.addChild(tr); //on l'ajoute au branchgroup tr.setCapability(tr.ALLOW_TRANSFORM_READ);

    s s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s o

    o o uu u rr r

    cc c ee e

    jj j aa avv v a

    a a

  • - - 30 - -

    tr.setCapability(tr.ALLOW_TRANSFORM_WRITE); //va permettre de transformer en temps réel(souris) tr.setCapability(tr.ALLOW_CHILDREN_READ); //et de lire le nbre d'enfants myMouseRotate = new MouseRotate(); //affecte l'action "rotate" de la souris (click) au TR myMouseRotate.setTransformGroup(tr); myMouseRotate.setSchedulingBounds(new BoundingSphere()); scene.addChild(myMouseRotate); set_factors(5,5); //règle les facteurs X et Y de la souris myMouseTranslate = new MouseTranslate(); //affecte l'action "translate" de la souris (shift+click) au TR myMouseTranslate.setTransformGroup(tr); myMouseTranslate.setSchedulingBounds(new BoundingSphere()); scene.addChild(myMouseTranslate); myMouseZoom = new MouseZoom(); myMouseZoom.setTransformGroup(tr); //affecte l'action "zoom" de la souris (alt+click) au TR myMouseZoom.setSchedulingBounds(new BoundingSphere()); scene.addChild(myMouseZoom); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0),100.0); bg = new Background(properties[BG_COLOR],properties[BG_COLOR],properties[BG_COLOR]); bg.setApplicationBounds(bounds); tr.addChild(bg); } /** * Réglage des facteurs X et Y de la souris */ public void set_factors(int x,int y) { properties[X_FACTOR]=x; properties[Y_FACTOR]=y; myMouseRotate.setFactor(properties[X_FACTOR]/1000.0,properties[Y_FACTOR]/1000.0); //changement des facteurs X et Y } /** * Reset du point de vue de l'univers virtuel. */ public void reset() { Transform3D trans_g= new Transform3D(); trans_g.set(new Vector3f(0,0,-19)); //dézoome Transform3D trans_r= new Transform3D(); trans_r.rotX(3.14); //rotation X de 180° trans_g.mul(trans_r); tr.setTransform(trans_g); } /** * Permet de définir une propriété de la visualisation * du nuage. * @param pro numéro de la propriété
    * @param val valeur boléenne */ public void set_properties(int pro,boolean val) { if (val) properties[pro]=1; else properties[pro]=0; } /** * Permet de définir une propriété de la visualisation * du nuage. * @param pro numéro de la propriété

    s s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s o

    o o uu u rr r

    cc c ee e

    jj j aa avv v a

    a a

  • - - 31 - -

    * @param val valeur entière */ public void set_properties(int pro,int val) { properties[pro]=val; } /** * Permet de lire une propriété de la visualisation * du nuage. * @param pro numéro de la propriété
    * @return valeur entière de la propriété */ public boolean get_properties(int pro) { if (properties[pro]>0) return true; else return false; } /** * Créé la visualisation 3D du nuage de points en fonction des propriétés définies * et l'attache à l'univers virtuel. * @param bmp nuage de points
    */ public void Create3dScene(Nuage bmp) { Shape3D circle; int t; Color c=Color.darkGray; int cluster; //cluster auquel appartient le point scene.detach(); //détache le BR pour pouvoir modifier le graphe scénique if (tr.numChildren()>0) tr.removeAllChildren(); //si le TR a des enfants alors on les supprime if (bmp.W3D==false && properties[X_FACTOR]>0) //nuage 2D? { reset(); set_factors(0,0); //on vire la rotation 3D à la souris } if (bmp.W3D==true ) //nuage 3D? { set_factors(5,5); } BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0),100.0); bg = new Background(properties[BG_COLOR],properties[BG_COLOR],properties[BG_COLOR]); bg.setApplicationBounds(bounds); tr.addChild(bg); //le fond de l'univers 3D Color3f light1Color = new Color3f(1f, 1f, 1f); Vector3f light1Direction = new Vector3f(2.0f, 2.0f, 2.0f); DirectionalLight light1= new DirectionalLight(light1Color, light1Direction);//lumiere directionnelle light1.setInfluencingBounds(bounds); tr.addChild(light1); AmbientLight ambientLight = new AmbientLight(new Color3f(.5f,.5f,.5f)); //lumière ambiante ambientLight.setInfluencingBounds(bounds); tr.addChild(ambientLight); PointArray lsa = new PointArray(bmp.N, GeometryArray.COORDINATES|GeometryArray.COLOR_3); Point3f pt = new Point3f(); PointAttributes p0= new PointAttributes(); p0.setPointSize(properties[POINTS_SIZE]); //définit la taille des points

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o u

    u u rr rcc c e

    e e jj j aa a

    vv v aa a

  • - - 32 - -

    Appearance app=new Appearance(); app.setPointAttributes(p0); bmp.Nna=0; for (int j = 0; j < bmp.N; j++) { pt.x = (float)bmp.Xx[j]/10.0f; //un nouveau point pt.y = (float)bmp.Xy[j]/10.0f; pt.z= (float)bmp.Xz[j]/10.0f; cluster=-1; for (int i=0;i=properties[THRESHOLD]) //> au seuil de défuzzyfication? { if (clusterbmp.u[i][cluster]) cluster=i; //degré d'app > à un autre > au seuil? } } if (cluster>=0) //le site apparient à un cluster? { if (cluster%8==0) c=Color.blue; if (cluster%8==1) c=Color.red; if (cluster%8==2) c=Color.green; if (cluster%8==3) c=Color.orange; if (cluster%8==4) c=Color.cyan; if (cluster%8==5) c=Color.magenta; if (cluster%8==6) c=Color.pink; if (cluster%8==7) c=Color.yellow; lsa.setCoordinate(j, pt); //ajoute un point de la couleur du cluster lsa.setColor(j, new Color3f(c)); } else { bmp.Nna++; //si le point n'est affecté à aucun des clusters alors on incrémente le nombre de points Non Affectés if (get_properties(UNCLASSIFIED)) { //si on doit afficher les points non affectés alors lsa.setCoordinate(j, pt); //ajoute un point de la couleur du cluster lsa.setColor(j, new Color3f(Color.darkGray)); } } } circle = new Shape3D(lsa,app); //nouveau Shape3D à partir du tableau de points if (get_properties(CENTROIDS)) //est-ce que on doit afficher les centroides? { app=new Appearance(); app.setMaterial(new Material(new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0.0f, 0.0f), new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0.0f, 0.0f), 1.0f)); for (t=0;t

  • - - 33 - -

    if (get_properties(BOX)) //affichage box? { ColoringAttributes ca= new ColoringAttributes(); ca.setColor(new Color3f(1-properties[BG_COLOR],1-properties[BG_COLOR],1-properties[BG_COLOR])); app=new Appearance(); app.setColoringAttributes(ca); float xmin=(float)bmp.minx/10.0f; float ymin=(float)bmp.miny/10.0f; float zmin=(float)bmp.minz/10.0f; float xmax=(float)bmp.maxx/10.0f; float ymax=(float)bmp.maxy/10.0f; float zmax=(float)bmp.maxz/10.0f; int numberOfPointsInStrip[] = { 8, 4, 4}; LineStripArray lbox = new LineStripArray(16, LineArray.COORDINATES, numberOfPointsInStrip); lbox.setCoordinate(0, new Point3d(xmin, ymin, zmin)); lbox.setCoordinate(1, new Point3d(xmax, ymin, zmin)); lbox.setCoordinate(2, new Point3d(xmax, ymax, zmin)); //avant lbox.setCoordinate(3, new Point3d(xmin, ymax, zmin)); lbox.setCoordinate(4, new Point3d(xmin, ymin, zmin)); lbox.setCoordinate(5, new Point3d(xmin, ymin, zmax)); lbox.setCoordinate(6, new Point3d(xmin, ymax, zmax)); //coté gauche lbox.setCoordinate(7, new Point3d(xmin, ymax, zmin)); lbox.setCoordinate(8, new Point3d(xmin, ymin, zmax)); lbox.setCoordinate(9, new Point3d(xmax, ymin, zmax)); lbox.setCoordinate(10, new Point3d(xmax, ymax, zmax)); //derriere lbox.setCoordinate(11, new Point3d(xmin, ymax, zmax)); lbox.setCoordinate(12, new Point3d(xmax, ymin, zmin)); lbox.setCoordinate(13, new Point3d(xmax, ymin, zmax)); //coté droit lbox.setCoordinate(14, new Point3d(xmax, ymax, zmax)); lbox.setCoordinate(15, new Point3d(xmax, ymax, zmin)); Shape3D box = new Shape3D(lbox,app); //la box tr.addChild(box); //on l'ajoute au TR } tr.addChild(circle); //ajoute le dessin du nuage 3D au TR simpleU.addBranchGraph(scene); //on rattache le BR qu'on avait détaché } } ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

  • - - 34 - -

    Nuage.java import java.awt.*; import java.awt.font.*; import java.io.*; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.universe.*; import com.sun.j3d.utils.behaviors.mouse.*; import javax.media.j3d.*; import java.util.StringTokenizer; import java.net.URL; /** * Classe permettant de charger et de partionner de nuages de points 2d ou 3d * @author Mickael ROUBAUD - SIP, université René Descartes PARIS * @version V3d.3 - juin 2003 */ public class Nuage { double Vx[],Vy[],Vz[]; //centroides des clusters double u[][]; //degrés d'appartenance int K; //nombre de clusters int N; //nombre de sites int Nna; //nombre de sites non affectés(points frontière) double Xx[],Xy[],Xz[]; //coordonnées des sites double minx,maxx,miny,maxy,minz,maxz; //coordonénees max du nuage String titre; //titre,nom du fichier private Canvas3D canvas3D; boolean W3D; //booléen nuage en 3D ou en 2D? /** * Cette méthode sert pour le calcul de la dimension (x,y,z) du nuage * @param float x coordonnée X du point * @param float y coordonnée Y du point * @param float z coordonnée Z du point * @param int t index du point */ private void dim(double x,double y,double z,int t) { if (x < minx || t==0) minx = x; if (y < miny || t==0) miny = y; if (z < minz || t==0) minz = z; if (x > maxx || t==0) maxx = x; if (y > maxy || t==0) maxy = y; if (z > maxz || t==0) maxz = z; } /** * Cette méthode charge un nuage de point à partir du générateur de nombres aléatoires * Elle affecte la mémoire nécessaire au stockage des coordonnées des sites, * et positionne N, nombre de sites. * @param nbp nombre de points du nuage */

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s o

    o o uu u rr r

    cc c ee e

    jj j aa avv v a

    a a

  • - - 35 - -

    public void load_from_random(int nbp) { int t; N=nbp; Xx=new double[N]; Xy=new double[N]; Xz=new double[N]; for (t=0;t

  • - - 36 - -

    do { //EOF? z = inStream.read(); if (z < 250 && z>-1) { Xx[t] = (double) (x - 160) / 3.2f; Xy[t] = (double) (y - 100) / 3.2f; Xz[t] = (double) (z - 128) / 3.0f; dim(Xx[t], Xy[t], Xz[t], t); //détection des dimensions du nuage t++; //rajoute un site } x++; if (x > 319) { y--; x = 0; } //fin de la bmp 320x200x8bpp } while (z>-1); N = t; inStream.close(); titre = nom; return true; } catch (java.io.IOException exception) { return false; } //problème? } /** * Cette méthode charge un nuage de point 2D à partir d'un fichier DATA 2D * Elle affecte la mémoire nécessaire au stockage des coordonnées des sites, * et positionne N, nombre de sites. * @param nom nom du nuage DATA * @return true si le fichier a été chargée * false sinon. */ private boolean load_from_data(String nom) throws IOException { URL file = new URL(nom) ; InputStream is = file.openStream(); int t; String s; double x,y; try { N=65000; Xx=new double[N]; Xy=new double[N]; Xz=new double[N]; x=0;y=0;t=0; BufferedReader inStream = new BufferedReader(new InputStreamReader(is)); do { s = inStream.readLine(); if (s!=null) { StringTokenizer st = new StringTokenizer(s, " "); String sx = st.nextToken(); String sy = st.nextToken(); Xx[t] = (10.0f * Float.parseFloat(sx)); Xy[t] = ( -10.0f * Float.parseFloat(sy)); Xz[t] = 0; //rajoute un site dim(Xx[t], Xy[t], Xz[t], t); //détection des dimensions du nuage t++; } } while (s!=null);//;inStream.ready()); //EOF? this.N=t; inStream.close(); titre=nom; return true;

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o u

    u u rr rcc c e

    e e jj j aa a

    vv v aa a

  • - - 37 - -

    } catch ( java.io.IOException exception) {return false;} //problème? } /** * Cette méthode charge un nuage de point à partir d'un fichier XYZ * Elle affecte la mémoire nécessaire au stockage des coordonnées des sites, * et positionne N, nombre de sites. * @param nom nom du nuage XYZ * @return true si le fichier a été chargée * false sinon. */ private boolean load_from_xyz(String nom) throws IOException { URL file = new URL(nom) ; InputStream is = file.openStream(); int t; double x,y; String s,sx,sy,sz; try { N=65000; Xx=new double[N]; Xy=new double[N]; Xz=new double[N]; x=0;y=0;t=0; BufferedReader inStream = new BufferedReader(new InputStreamReader(is)); do { s=inStream.readLine(); if (s!=null) if (s.indexOf(".")>0) { // if (t0)==false) sx = st.nextToken(); sy = st.nextToken(); sz = st.nextToken(); Xz[t] = (5.0f * Float.parseFloat(sx));//z x y Xx[t] = (-5.0f * Float.parseFloat(sy)) - 0.1f; Xy[t] = (-10.0f * Float.parseFloat(sz)); //rajoute un site dim(Xx[t], Xy[t], Xz[t], t); //détection des dimensions du nuage t++; } } while (s!=null); //EOF? this.N=t; inStream.close(); titre=nom; return true; } catch ( java.io.IOException exception) {return false;} //problème? } /** * Cette méthode k-partitionne un nuage de points suivant l'algorithme des K-Means. * L'espace mémoire nécessaire au stockage des coordonnées * des centroides et des degrés d'appartenance y est aussi réservé. * @param k nombre de clusters pour la segmentation */ public void clusterize(int k) { double maxu; int i,j; double d; double somme,sommey,sommex,sommez; u=new double[k][N];

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o u

    u u rr rcc c e

    e e jj j aa a

    vv v aa a

  • - - 38 - -

    Vx=new double[k]; Vy=new double[k]; Vz=new double[k]; K=k; /* for (i=0;i

  • - - 39 - -

    d.set(0, 0, Xx[xi] - Vx[xj]); d.set(0, 1, Xy[xi] - Vy[xj]); //vecteur distance if (W3D) d.set(0, 2, Xz[xi] - Vz[xj]); d_t = d.trans(); m = F_inv.mul(d); m=d_t.mul(m); return (Math.sqrt(det) / P) * Math.exp(m.get()/2.0); /* double x,y,vx,vy,aux; vx=Xx[xi]-Vx[xj]; vy=Xy[xi]-Vy[xj]; x=F_inv.get(0,0)*vx+F_inv.get(1,0)*vy; y=F_inv.get(0,1)*vx+F_inv.get(1,1)*vy; aux=x*vx+y*vy; // aux=aux*Math.sqrt(det) / P; aux=(Math.sqrt(det)/P)*Math.exp(aux/2.0); return (aux); */ } /** * Cette méthode k-partitionne un nuage de points suivant FMLE. * L'espace mémoire nécessaire au stockage des coordonnées * des centroides et des degrés d'appartenance y est aussi réservé. * @param k nombre de clusters pour la segmentation */ public void exp_clusterize(int k) { Matrix [] F; double maxu=1; int i,j,t,z,iter; Matrix [] F_inv=new Matrix[k]; //Matrix [] F=new Matrix[k]; Matrix d,d_t,m; double det[]=new double[k]; double somme,sommex,sommey,sommez,de; double P[]=new double[k]; double dist,dist1=0; System.err.println("expcluster"); for (int r=0;r

  • - - 40 - -

    { somme += u[i][j]; d.set(0, 0, Xx[j] - Vx[i]); d.set(0, 1, Xy[j] - Vy[i]); if (W3D) d.set(0, 2, Xz[j] - Vz[i]); //vecteur distance d_t = d.trans(); //transposé m = d.mul(d_t); m = m.mul(u[i][j]); F[i] = F[i].add(m); } P[i] = somme / ((double)N); //probabilité de sélectionner le ième cluster F[i] = F[i].div(somme); det[i] = F[i].det(); F_inv[i] = F[i].inv(); //fin calcul matrice de cov } iter=0; while (maxu>0.001 && iter

  • - - 41 - -

    det[i] = F[i].det(); F_inv[i] = F[i].inv(); //fin calcul matrice de cov } } //le maxi des uij est < à epsilone System.err.println("fin expcluster"); } /** * Cette méthode calcule le critère DPA d'un nuage de points * segmenté. */ public double dpa() { int i,j; Matrix cov,cov_inv,d; double det; //determinant de la mat de cov if (W3D) cov=new Matrix(3,3); //matrice de covariance floue 3D else cov=new Matrix(2,2); //matrice de cov floue 2D double somme; double dpa=0; System.err.println("dpa"); if (W3D) d = new Matrix (1,3); //vecteur distance en 3D else d = new Matrix (1,2); //distance en 2D Matrix d_t; //transposé Matrix m; for (j=0;j

  • - - 42 - -

    dpa+=somme/det; //densité moyenne de partition } dpa=dpa/K; System.err.println("fin dpa"); return dpa; } /** * Cette méthode recherche la segmentation optimale d'un nuage de points et renvoie * le nombre de clusters. * @return k nombre de clusters optimal */ public int optimal() { double dpa_derive=0,dpa_avant=0,crit; int t; for (t=1;t

  • - - 43 - -

    Matrix.java import java.awt.*; import java.io.*; /** * Classe de gestion de matrices 2x2 et 3x3 * @author Mickael ROUBAUD - SIP, université René Descartes PARIS * @version V3d.3 - juin 2003 */ public class Matrix { private double m[][]; private int rows,cols; /** * Constructeur...reserve l'espace mémoire pour la matrice * et met tous les coefficients à 0 * @param x nombre de colonnes de la matrice
    * @param y nombre de lignes de la matrice */ public Matrix(int x,int y) { int i,j; rows=y; cols=x; m=new double [cols][rows]; for (i=0;i

  • - - 44 - -

    /** * Affecte le coefficient c,r de la matrice * @param c colonne
    * @param r ligne
    * @param val valeur du coefficient */ public void set(int c,int r,double val) { m[c][r]=val; } /** * Additionne 2 matrices * @param m2 matrice à ajouter à l'instance * @return matrice résultante de la somme */ public Matrix add(Matrix m2) { Matrix cov_add = new Matrix (cols,rows); for (int i=0;i

  • - - 45 - -

    /** * Multiplie la matrice par un réel * @param mul réel multiplicateur de la matrice * @return matrice résultante de la multiplication */ public Matrix mul(double mul) { Matrix cov_mul = new Matrix (cols,rows); for (int i=0;i

  • - - 46 - -

    x.m[imax][j] = tmp; } // combinaison lineaire double pivot; for ( int i = r+1; i < N; i++ ) { pivot = - aa.m[i][r] / aa.m[r][r]; for ( int j = r; j < N; j++ ) { aa.m[i][j] += pivot * aa.m[r][j]; } for ( int j = 0; j < x.cols; j++ ) { x.m[i][j] += pivot * x.m[r][j]; } } } // Fin de la descente. Remontée. for ( int r = N-1; r >= 0; r-- ) { for ( int i = 0; i < r; i++ ) { double pivot = - aa.m[i][r] / aa.m[r][r]; for ( int j = 0; j < x.cols; j++ ) { x.m[i][j] += pivot * x.m[r][j]; } } for ( int j = 0; j < x.cols; j++ ) { x.m[r][j] /= aa.m[r][r]; } } return x; } public void print() { String ligne; for (int i=0;i

  • - - 47 - -

    constants.java interface constants { public static final int CENTROIDS=1; public static final int UNCLASSIFIED=2; public static final int BOX=3; public static final int POINTS_SIZE=4; public static final int X_FACTOR=5; public static final int Y_FACTOR=6; public static final int BG_COLOR=7; public static final int THRESHOLD=8; } ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

  • - - 48 - -

    Cadre1.java import java.applet.Applet; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.awt.geom.*; import java.awt.font.*; import java.io.*; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.universe.*; import com.sun.j3d.utils.behaviors.mouse.*; import javax.media.j3d.*; /** * Applet de partitionnement de nuages de points 2d ou 3d. * @author Mickael ROUBAUD - SIP, université René Descartes PARIS * @version V3d.3 - juin-aout 2003 */ public class Cadre1 extends Applet { JPanel contentPane; BorderLayout borderLayout1 = new BorderLayout(); //private Graphics g; // la feuille de desin de l'applet /** * Classe interne définissant la zone d'affichage "info" en bas et son comportement */ class Info extends Canvas { public Info() { setForeground(Color.white); setBackground(Color.black); setFont(new Font("arial",Font.BOLD,9)); setSize(200,15); } public void paint(Graphics g) { String aff; Graphics g2= info.getGraphics(); g2.clearRect(0,0,info.getWidth(),100); //on efface tout avant de reécrire if (bmp.W3D) aff="Nuage 3D"; else aff="Nuage 2D"; g2.drawString(aff+" - "+bmp.K + " clusters - " +bmp.N + " points - "+ Math.round(100*(float)bmp.Nna/(float)bmp.N) + "% points non affectés",10,10); } } Nuage bmp; Barre barre; Info info; String param,upload; /**Construire le cadre*/ public void init() { try { bmp= new Nuage(); View3d v=new View3d(this); //créé l'environnement 3D showStatus("INITIALISATION"); param = getParameter("nom"); //images du site

    s s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s o

    o o uu u rr r

    cc c ee e

    jj j aa avv v a

    a a

  • - - 49 - -

    upload = getParameter("upload"); //est-ce un upload? Container content=this; info= new Info(); content.add(info,"South"); barre=new Barre(bmp,this,v); //créé la barre de boutons content.add(barre,"North"); //ajoute la barre à la fenetre repaint(); } catch(Exception e) { e.printStackTrace(); } } public void paint(Graphics g) { barre.image.repaint(); info.repaint(); showStatus("Fuzzy K means - SIP 2003 - " + bmp.titre); } } ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e

  • - - 50 - -

    Barre.java import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.awt.geom.*; import java.awt.font.*; import java.io.*; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.universe.*; import com.sun.j3d.utils.behaviors.mouse.*; import javax.media.j3d.*; import java.util.StringTokenizer; import java.net.URL; /** * L'IHM de l'application de partitionnement. * @author Mickael ROUBAUD - SIP, université René Descartes PARIS * @version V3d.4 - juin-aout 2003 */ public class Barre extends JPanel implements constants { /** * Classe interne définissant la zone d'affichage de l'image et son comportement */ class Im extends Canvas { public void paint(Graphics g) //affichage de l'image { this.setBackground(Color.black); Graphics g2=getGraphics(); int h=getHeight(); int w=(int)((fim.getWidth(framep))*(float)(fim.getHeight(framep)/h)); //garde un rapport w/h constant int l=(getWidth()-w)/2; //centre l'image g2.drawImage(fim,l,1,w,h,framep); //affiche l'image } } //variables globale de la classe Im image; //la partie "image" de la barre Cadre1 framep; // Image fim; //l'image chargée JButton bkmeans; /** * Constructeur.Créé une barre d'outils avec les boutons, cases et listes déroulantes. * Attache les "Action Listener" à ces composants pour permettre d'interagir * avec la classe de partitionnement. * @param bmpp nuage de points actuellement traité * @param framep l'applet de partitionnement * @param v univers virtuel de visualisation du nuage de points */ public Barre(final Nuage bmpp,final Cadre1 framep,final View3d v) { this.framep=framep; Panel gauche= new Panel(); Panel bas= new Panel(); image=new Im(); final JButton bfmle,bquitter,breset; //les objets de l'interface final JComboBox aliste,bliste,sliste,nliste,pliste,fliste,tliste; final JCheckBox casec,casea,caseb,casex,casey;

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o

    uu u rr r cc c

    ee e jj j aa a

    vv v aa a

    ss s oo o u

    u u rr rcc c e

    e e jj j aa a

    vv v aa a

  • - - 51 - -

    this.setPreferredSize(new Dimension(600, 228)); this.setMinimumSize(new Dimension(1000,228)); this.setLayout(new BorderLayout()); gauche.setLayout(new BoxLayout(gauche, BoxLayout.Y_AXIS)); gauche.setBackground(Color.LIGHT_GRAY); bas.setLayout(new GridLayout(2,2)); bas.setBackground(Color.LIGHT_GRAY); this.add("West",gauche); //ajoute les panel à la Barre this.add("South",bas); this.add("Center",image); aliste = new JComboBox(); //type d'algo aliste.addItem("C-Moyennes Floues"); aliste.addItem("C-Moyennes Floues amélioré"); aliste.setSelectedIndex(0); aliste.setMaximumSize(new Dimension(200,20)); gauche.add(aliste); bliste = new JComboBox(); //K de départ for (int i = 1; i < 10; i++) bliste.addItem("de K=" + i); bliste.setSelectedIndex(1); bliste.setMaximumSize(new Dimension(80,20)); gauche.add(bliste); tliste = new JComboBox(); //K d'arrivée for (int i = 1; i < 10; i++) tliste.addItem("à K=" + i); tliste.setSelectedIndex(1); tliste.setMaximumSize(new Dimension(80,20)); gauche.add(tliste); bfmle = new JButton("Continuer (k++)"); bfmle.setMaximumSize(new Dimension(150,20)); gauche.add(bfmle); bfmle.setAlignmentX(0.5f); sliste = new JComboBox(); //seuil de defuzzy for (int i = 1; i < 10; i++) sliste.addItem("S=0." + i); sliste.setSelectedIndex(4); sliste.setMaximumSize(new Dimension(65,20)); gauche.add(sliste); bkmeans = new JButton("GO"); bkmeans.setForeground(Color.red); gauche.add(bkmeans); bkmeans.setAlignmentX(0.5f); nliste = new JComboBox(); if (framep.upload==null) //c'est un upload? { StringTokenizer st = new StringTokenizer( framep.param, "," ); //non alors sépare les noms passés en param do { nliste.addItem(st.nextToken()); //ajoute chaque nom à la liste de fichiers } while (st.hasMoreTokens()); gauche.add(nliste); nliste.setMaximumSize(new Dimension(200,20)); } else nliste.addItem(framep.upload); //sinon,on ajoute que le fichier uploadé casec = new JCheckBox("Centroïdes",true); casec.setBackground(Color.LIGHT_GRAY); casec.setMaximumSize(new Dimension(65,20)); bas.add(casec); casea = new JCheckBox("Sites NonAffectés",true); casea.setBackground(Color.LIGHT_GRAY); casea.setMaximumSize(new Dimension(65,20)); bas.add(casea); caseb = new JCheckBox("Box",true);

    s s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s o

    o o uu u rr r

    cc c ee e

    jj j aa avv v a

    a a

  • - - 52 - -

    caseb.setBackground(Color.LIGHT_GRAY); caseb.setMaximumSize(new Dimension(65,20)); bas.add(caseb); pliste = new JComboBox(); for (int i = 1; i < 6; i++) pliste.addItem("Taille=" + i); pliste.setSelectedIndex(1); pliste.setMaximumSize(new Dimension(65,20)); bas.add(pliste); breset = new JButton("RAZ 3D"); breset.setMaximumSize(new Dimension(65,20)); bas.add(breset); casex = new JCheckBox("X",true); casex.setBackground(Color.LIGHT_GRAY); casex.setMaximumSize(new Dimension(65,20)); bas.add(casex); casey = new JCheckBox("Y",true); casey.setBackground(Color.LIGHT_GRAY); casey.setMaximumSize(new Dimension(65,20)); bas.add(casey); fliste = new JComboBox(); fliste.addItem("Fond Blanc"); fliste.addItem("Fond Noir"); fliste.setSelectedIndex(1); fliste.setMaximumSize(new Dimension(65,20)); bas.add(fliste); //définition des evènements associés aux objets de l'interface class barreal implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getSource()==bkmeans) //si on lance le partitionnement (bouton GO) { double dpa_derive=0,dpa_avant=0,crit; int t; Graphics g2=framep.info.getGraphics(); framep.setCursor(new Cursor(Cursor.WAIT_CURSOR)); //change la souris en sablier framep.showStatus("CALCUL"); //affiche calcul dans le status du browser for (t=1+bliste.getSelectedIndex();t

  • - - 53 - -

    } if (e.getSource()==sliste) //si on change de seuil { v.set_properties(THRESHOLD,sliste.getSelectedIndex()+1); framep.repaint(); v.Create3dScene(bmpp); } if (e.getSource()==nliste) //si on change de fichier { String fich; fich= nliste.getSelectedItem().toString(); load_image(fich+".jpg"); bmpp.load_file(framep.getCodeBase() +fich); bmpp.clusterize(1+bliste.getSelectedIndex()); framep.repaint(); framep.info.repaint(); v.Create3dScene(bmpp); image.repaint(); } if (e.getSource()==casec) //on change l'affichage des centroides { v.set_properties(CENTROIDS,casec.isSelected()); framep.repaint(); v.Create3dScene(bmpp); } if (e.getSource()==casea) //affichage des sites non affectés { v.set_properties(UNCLASSIFIED,casea.isSelected()); framep.repaint(); v.Create3dScene(bmpp); } if (e.getSource()==pliste) //change la taille des points { v.set_properties(POINTS_SIZE,pliste.getSelectedIndex()+1); framep.repaint(); v.Create3dScene(bmpp); } if (e.getSource()==bliste || e.getSource()==tliste) //si on modifie de ou à { if (bliste.getSelectedIndex()>tliste.getSelectedIndex()) { tliste.setSelectedIndex(bliste.getSelectedIndex()); //pour que on ai tjours de>à repaint(); } } if (e.getSource()==caseb) //affichage de la boite? { v.set_properties(BOX,caseb.isSelected()); framep.repaint(); v.Create3dScene(bmpp); }

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s o

    o o uu u rr r

    cc c ee e

    jj j aa avv v a

    a a

  • - - 54 - -

    if (e.getSource()==breset) v.reset(); //bouton reset vue 3D if ((e.getSource()==casex || e.getSource()==casey) && bmpp.W3D) //uniquement si le nuage est en 3D { if (casex.isSelected() && casey.isSelected()) v.set_factors(5,5); //gestion des cases X et Y if (casex.isSelected() && !casey.isSelected()) v.set_factors(5,0); if (!casex.isSelected() && casey.isSelected()) v.set_factors(0,5); if (!casex.isSelected() && !casey.isSelected()) v.set_factors(0,0); } if (e.getSource()==fliste) //gestion du background de la vue { if (fliste.getSelectedIndex()==0) v.set_properties(BG_COLOR,1); if (fliste.getSelectedIndex()==1) v.set_properties(BG_COLOR,0); framep.repaint(); v.Create3dScene(bmpp); } if (e.getSource()==bfmle) //bouton continuer K++ { framep.setCursor(new Cursor(Cursor.WAIT_CURSOR)); framep.showStatus("CALCUL"); bmpp.clusterize(++bmpp.K); if (aliste.getSelectedIndex()==1) bmpp.exp_clusterize(bmpp.K); framep.repaint(); framep.info.repaint(); v.Create3dScene(bmpp); framep.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } } barreal bal= new barreal(); bkmeans.addActionListener(bal); bfmle.addActionListener(bal); //on ajoute les comportements aux objets sliste.addActionListener(bal); nliste.addActionListener(bal); casec.addActionListener(bal); casea.addActionListener(bal); caseb.addActionListener(bal); pliste.addActionListener(bal); breset.addActionListener(bal); casex.addActionListener(bal); casey.addActionListener(bal); fliste.addActionListener(bal); bliste.addActionListener(bal); tliste.addActionListener(bal); nliste.setSelectedIndex(0); //charge le 1er fichier au démarrage } /** * Charge une image et l'affiche dans la zone consacrée de la barre. * Si le fichier passé en paramètre n'existe pas, on affichera le logo SIP * @param nom nom de l'image à afficher */ private void load_image(String nom) { try{ URL file = new URL(framep.getCodeBase()+nom) ; //url de l'image try { InputStream is = file.openStream(); //tente d'ouvrir l'image } catch(java.io.IOException exp2) {nom="logosip.jpg";}; //pas trouvé le fichier! } catch(java.net.MalformedURLException exp) {}; //alors on prend le logo

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a

    ss s o

    o o uu u rr r

    cc c ee e

    jj j aa avv v a

    a a

  • - - 55 - -

    fim =framep.getImage(framep.getDocumentBase(),nom); //charge l'image (logo sip ou image stéréo) while (fim.getHeight(framep)==-1) {}; //attends le chargement complet de l'image } }

    ss s oo ouu u r

    r r cc cee e

    jj j aa avv v a

    a a