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

publicité
RAPPORT DE STAGE
2 année du cycle ingénieur
ème
MAJEURE :
ELEVE-INGENIEUR :
‰ Systèmes d’Information & Réseaux
„ Systèmes Embarqués
‰ Télécommunications & Réseaux
Nom : ROUBAUD
Prénom : Mickaël
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
Segmentation et visualisation « on-line » de nuages de
points 3D
Mickael ROUBAUD
responsable chercheur :Nicolas Loménie
avril-août
2003
__________________________________________________Sommaire
Le laboratoire « Systemes intelligents de Perception »
Fiche d’identité
Activités du laboratoire
3
4
La mission
4
Cahier des charges
La reconstruction 3d
Planning
5
6
Description du travail
L’environnement de programmation
L’algorithme de partitionnement
Recherche de la partition optimale
Premiers résultats
Visualisation 3d
Classes
7
8
11
12
13
15
Applet développée
L’interface
Utilisation
Fichiers pris en charge
Aide java vm
Benchmarks
16
19
20
21
22
Conclusion
23
Annexes
Glossaire
Resultats
Sources
Code
25
26
28
29
Les mots en italique suivis de * sont définis dans le glossaire, p.25
--1--
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…
--2--
___________________________________________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
<<L
LA
AB
BO
OS
SIIP
P
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
--3--
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.
--4--
__________________________________________________________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 avec objet reconstruit
Nuage de points 3D extrait et partitionné*
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.
--5--
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 :
Moteur d’interférence
Connaissances
Paramètres de visualisation
Chargement fichier
Visualisation
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
14
17
22
25
avril
avril
avril
avril
PREVISION
EFFECTUE
début du stage
début du stage
1ère application graphique en java
1ère application java3D
routine de segmentation 2D validée
routine de détection de l’optimal
validée, nom des fonctions, 1ère
javadoc, découpage en classes
1ère version de la segmentation
améliorée
choix définitif de Java3D pour la
visualisation
intégration de java3D à l’application de
segmentation
1ère version applet
kmeans 2D améliorée validée
kmeans 3D améliorée validée
nouvelle disposition de l’interface
10 mai
15 mai
maîtrise java,
segmentation commencée
21 mai
23 mai
28 mai
26 juin
30 juin
21 juillet
15 août
24 août
30 septembre
kmeans 2D validée
kmeans 3D validée
fin du stage
remise du rapport
--6--
remise du rapport
_______________________________________________________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.
--7--
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.
--8--
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)
--9--
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.
- - 10 - -
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.
oui
K=Kmin
K-partitionne
Critère?
Renvoyer la K-1 partition
non
oui
K=K+1
K<=Kmax?
Partition optimale
non trouvée
Pour cette application, j’ai utilisé le critère DPA (Density Partition Average) associé à sa
dérivée : pour chaque partition on calcule le DPA du nuage et sa dérivée. Si cette dérivée est
décroissante on considère que la partition (K-1) est valide. Nous recherchons en fait le
premier extremum de la dérivée du DPA.
Pour calculer le DPA, il faut d’abord effectuer la « somme des sites centraux » :
(c’est à dire la somme des uij de chaque site contenu dans la boule de rayon 1 centrée sur Vi)
La densité de partition moyenne (DPA) :
Je me suis servi de la console de deboggage pour afficher les données relatives à la recherche
de l’optimal :
On peut y voir le calcul du DPA, le nombre de clusters, la valeur de la dérivée et la position
dans l’espace des centroides.
Ceci m’a facilité la tâche de recherche de problèmes et aussi permis de tracer les courbes de
DPA en fonction de K.
- - 11 - -
Premiers résultats
50
40
30
Dpa
20
Dérivée
10
0
-10
1
2
3
4
5
6
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)
12
10
8
dpa
6
dérivée
4
2
0
0
1
2
3
4
5
6
7
8
9
Nuage 3D de 2063 points (fichier 3d.bmp). Là aussi l’algorithme obtient le résultat correct, à
savoir une 3-partition.
20
15
10
dpa
d é riv é e
5
0
0
1
2
3
4
5
6
7
8
9
-5
L’algorithme trouve que la 2-partition est optimale (fichier 3d2.bmp de 1472 points).
- - 12 - -
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 :
VirtualUniverse (univers virtuel)
Locale (scène)
BG
BranchGroup (groupe de branchement)
TG
TransformGroup (groupe de transformation)
S
BG
BG
S
TG
Shape (objet)
NodeComponent
Relation parent-enfant
S
référence
Apparence
Géométrie
Apparence
Géométrie
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.
- - 13 - -
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.
simpleU
BG scene
TG
TG tr_c
TG tr_c
S
box
S
S
centre
centre
tr
S
Comportement
circle
myMouseRotate,myMouseTranslate,
myMouseZoom
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.
- - 14 - -
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
Cadre1.java
Constants.java
Matrix.java
Nuage.java
View3d.java
barre de boutons (moteur d’interférence)
gestion de l’interface graphique (affichage)
définition des constantes spécifiques à l’application
calcul sur les matrices
chargement et segmentation de nuages de points 2D et 3D
affichage de nuages de points 2D et 3D
- - 15 - -
_______________________________________________________L’applet
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
- - 16 - -
développée
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.
- - 17 - -
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.
- - 18 - -
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:
<applet code=Cadre1.class width=90% height=95%>
<param name=nom value="img/3d.bmp,img/lama/image3d.5.085.xyz">
</applet>
-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.
- - 19 - -
<applet code=Cadre1.class width=90% height=95%>
<param name=nom value="img/3d.bmp,img/lama/image3d.5.085.xyz">
<param name=upload value="tmp.bmp">
</applet>
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
- - 20 - -
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.
- - 21 - -
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
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
nuage
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é).
- - 22 - -
______________________________________________________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.
- - 23 - -
ANNEXES
- - 24 - -
___________________________________________________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
- - 25 - -
_________________________________________________________________Résultats
Comparatif : partitionnement avec distance exponentielle contre partitionnement avec
distance euclidienne.
distance exponentielle
distance euclidienne
- - 26 - -
- - 27 - -
_________________________________________________________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
- - 28 - -
_______________________________________________________Code
source Java
ssoouurrccee jjaavvaa
View3d.java
import
import
import
import
import
import
import
import
import
import
java.applet.Applet;
java.awt.BorderLayout;
java.awt.Frame;
java.awt.*;
com.sun.j3d.utils.applet.MainFrame;
com.sun.j3d.utils.geometry.*;
com.sun.j3d.utils.universe.*;
com.sun.j3d.utils.behaviors.mouse.*;
javax.media.j3d.*;
javax.vecmath.*;
oouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
/**
* 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
private
private
private
private
private
private
private
private
SimpleUniverse simpleU;
BranchGroup scene;
Canvas3D canvas3D;
TransformGroup tr;
int properties[];
MouseRotate myMouseRotate;
MouseTranslate myMouseTranslate;
MouseZoom myMouseZoom;
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);
centroides?
set_properties(UNCLASSIFIED,true);
points non affectés?
set_properties(BOX,true);
box
set_properties(POINTS_SIZE,2);
set_properties(BG_COLOR,0);
set_properties(THRESHOLD,5);
//afficher les
//afficher les
//affichage
tr=new TransformGroup();
tranformgrooup
this.reset();
de vue
scene.addChild(tr);
au branchgroup
tr.setCapability(tr.ALLOW_TRANSFORM_READ);
- - 29 - -
//création
//raz du point
//on l'ajoute
ssoouurrccee jjaavvaa
tr.setCapability(tr.ALLOW_TRANSFORM_WRITE);
de transformer en temps réel(souris)
tr.setCapability(tr.ALLOW_CHILDREN_READ);
le nbre d'enfants
//va permettre
myMouseRotate = new MouseRotate();
l'action "rotate" de la souris (click) au TR
myMouseRotate.setTransformGroup(tr);
myMouseRotate.setSchedulingBounds(new BoundingSphere());
scene.addChild(myMouseRotate);
set_factors(5,5);
facteurs X et Y de la souris
//affecte
myMouseTranslate = new MouseTranslate();
l'action "translate" de la souris (shift+click) au TR
myMouseTranslate.setTransformGroup(tr);
myMouseTranslate.setSchedulingBounds(new BoundingSphere());
scene.addChild(myMouseTranslate);
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
myMouseZoom = new MouseZoom();
myMouseZoom.setTransformGroup(tr);
l'action "zoom" de la souris (alt+click) au TR
myMouseZoom.setSchedulingBounds(new BoundingSphere());
scene.addChild(myMouseZoom);
//règle les
//affecte
//affecte
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));
Transform3D trans_r= new Transform3D();
trans_r.rotX(3.14);
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é<br>
* @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é<br>
oouurrccee jjaavvaa
//et de lire
- - 30 - -
//dézoome
//rotation X de
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
* @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é<br>
* @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<br>
*/
public void Create3dScene(Nuage bmp)
{
Shape3D circle;
int t;
Color c=Color.darkGray;
int cluster;
//cluster
auquel appartient le point
ssoouurrccee jjaavvaa
scene.detach();
BR pour pouvoir modifier le graphe scénique
if (tr.numChildren()>0) tr.removeAllChildren();
des enfants alors on les supprime
if (bmp.W3D==false && properties[X_FACTOR]>0)
//nuage 2D?
{
reset();
set_factors(0,0);
rotation 3D à la souris
}
urrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
if (bmp.W3D==true )
{
set_factors(5,5);
}
//détache le
//si le TR a
//on vire la
//nuage 3D?
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
- - 31 - -
oouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
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;
point
pt.y = (float)bmp.Xy[j]/10.0f;
pt.z= (float)bmp.Xz[j]/10.0f;
//un nouveau
cluster=-1;
for (int i=0;i<bmp.K;i++)
//pour tous
les clusters
{
if (10*bmp.u[i][j]>=properties[THRESHOLD])
//> au seuil
de défuzzyfication?
{
if (cluster<0) cluster = i;
else if (bmp.u[i][j]>bmp.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<bmp.K;t++)
{
Transform3D trans= new Transform3D();
//on translate
le centre sur le centroide
trans.set(new
Vector3f((float)bmp.Vx[t]/10.0f,(float)bmp.Vy[t]/10.0f,(float)bmp.Vz[t]/10.0f));
TransformGroup tr_c=new TransformGroup(trans);
//TR du
cluster
float taille_centr=0.001f*(float)((bmp.maxx-bmp.minx)+(bmp.maxy-bmp.miny)+(bmp.maxzbmp.minz))/3.0f;//moyenne des dimensions du nuage
Sphere centre= new Sphere(taille_centr);
//la sphere du
centroide
centre.setAppearance(app);
tr_c.addChild(centre);
tr.addChild(tr_c);
//on ajoute le
TR du cluster au TR général
}
}
- - 32 - -
ssoouurrccee jjaavvaa
if (get_properties(BOX))
//affichage
box?
ColoringAttributes ca= new ColoringAttributes();
ca.setColor(new Color3f(1-properties[BG_COLOR],1-properties[BG_COLOR],1properties[BG_COLOR]));
app=new Appearance();
app.setColoringAttributes(ca);
float
float
float
float
float
float
xmin=(float)bmp.minx/10.0f;
ymin=(float)bmp.miny/10.0f;
zmin=(float)bmp.minz/10.0f;
xmax=(float)bmp.maxx/10.0f;
ymax=(float)bmp.maxy/10.0f;
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));
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
{
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));
ssoouurrccee jjaavvaa
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,
lbox.setCoordinate(13, new Point3d(xmax,
lbox.setCoordinate(14, new Point3d(xmax,
lbox.setCoordinate(15, new Point3d(xmax,
Shape3D box = new Shape3D(lbox,app);
tr.addChild(box);
ymin,
ymin,
ymax,
ymax,
zmin));
zmax)); //coté droit
zmax));
zmin));
//la box
//on l'ajoute
ssoouurrccee jjaavvaa
au TR
}
tr.addChild(circle);
dessin du nuage 3D au TR
simpleU.addBranchGraph(scene);
le BR qu'on avait détaché
}
}
//ajoute le
//on rattache
- - 33 - -
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
Nuage.java
import
import
import
import
import
import
import
import
import
import
java.awt.*;
java.awt.font.*;
java.io.*;
com.sun.j3d.utils.applet.MainFrame;
com.sun.j3d.utils.geometry.ColorCube;
com.sun.j3d.utils.universe.*;
com.sun.j3d.utils.behaviors.mouse.*;
javax.media.j3d.*;
java.util.StringTokenizer;
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[];
des clusters
double u[][];
d'appartenance
int K;
clusters
int N;
sites
int Nna;
sites non affectés(points frontière)
double Xx[],Xy[],Xz[];
des sites
double minx,maxx,miny,maxy,minz,maxz;
//coordonénees max du nuage
String titre;
fichier
private Canvas3D canvas3D;
boolean W3D;
nuage en 3D ou en 2D?
//centroides
//degrés
//nombre de
//nombre de
//nombre de
//coordonnées
//titre,nom du
//booléen
/**
* 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;
ssoouurrccee jjaavvaa
}
/**
* 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
*/
cee jjaavvaa
- - 34 - -
public void load_from_random(int nbp)
{
int t;
ssoouurrccee jjaavvaa
N=nbp;
Xx=new double[N];
Xy=new double[N];
Xz=new double[N];
for (t=0;t<nbp;t++)
{
Xx[t]=100*(double)(Math.random()-0.5f);
Xy[t]=100*(double)(Math.random()-0.5f);
Xz[t]=100*(double)(Math.random()-0.5f);
dim(Xx[t],Xy[t],Xz[t],t);
des dimensions du nuage
}
//détection
titre="Random";
urrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
/**
* Cette méthode sert à "aiguiller" lors du chargement de fichier, vers les fonctions
* de chargement en fonction de l'extension.
* @param nom nom du fichier nuage de points à charger
*/
public void load_file(String nom)
{
try {
if (nom.indexOf(".bmp")>0) load_from_bmp(nom);
if (nom.indexOf(".xyz")>0) load_from_xyz(nom);
if (nom.indexOf(".data")>0) load_from_data(nom);
if (maxz-minz==0) W3D=false; else W3D=true;
//le nuage
est-il en 3D?
}
catch(Exception ex) {
ex.printStackTrace();
}
}
/**
* Cette méthode charge un nuage de point à partir d'une BMP 320x200x8bpp
* Elle affecte la mémoire nécessaire au stockage des coordonnées des sites,
* et positionne N, nombre de sites.
* @param nom nom de l'image BMP
* @return
<code>true</code> si l'image a été chargée
*
<code>false</code> sinon.
*/
private boolean load_from_bmp(String nom) throws IOException
{
URL file = new URL(nom) ;
InputStream is = file.openStream();
DataInputStream inStream;
int t;
boolean e=false;
fichier atteinte?
float x, y, z;
try {
inStream = new DataInputStream(is); //ouvre le fichier
N = 64000;
Xx = new double[N];
Xy = new double[N];
Xz = new double[N];
inStream.skipBytes(1078); //saute l'entete
x = 0;
y = 200;
t = 0;
- - 35 - -
//fin du
{ //EOF?
z = inStream.read();
if (z < 250 && z>-1) {
Xx[t] = (double) (x - 160) /
Xy[t] = (double) (y - 100) /
Xz[t] = (double) (z - 128) /
dim(Xx[t], Xy[t], Xz[t], t);
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;
ssoouurrccee jjaavvaa
do
3.2f;
3.2f;
3.0f;
//détection des dimensions du nuage
}
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
<code>true</code> si le fichier a été chargée
*
<code>false</code> 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));
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
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++;
}
}
//EOF?
while (s!=null);//;inStream.ready());
this.N=t;
inStream.close();
titre=nom;
return true;
jjaavvaa
- - 36 - -
} 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
<code>true</code> si le fichier a été chargée
*
<code>false</code> 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 (t<50) System.err.println(s);
StringTokenizer st = new StringTokenizer(s, " ");
sx = st.nextToken();
if ((sx.indexOf(".")>0)==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);
this.N=t;
inStream.close();
titre=nom;
return true;
} catch ( java.io.IOException exception)
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
//EOF?
{return false;}
//problème?
}
ssoouurrccee jjaavvaa
/**
* 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;
urrccee jjaavvaa
u=new double[k][N];
- - 37 - -
Vx=new double[k];
Vy=new double[k];
Vz=new double[k];
K=k;
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
/*
for (i=0;i<K;i++)
{
Vx[i]=(maxx-minx)/2.0f+minx;//*(float)Math.random()
Vy[i]=(maxy-miny)/2.0f+miny;
Vz[i]=(maxz-minz)/2.0f+minz;
}
*/
do
{
maxu=0.0;
//uij maxi
pour stopper le calcul
for (i=0;i<K;i++)
//pour chaque
cluster
{
for (j=0;j<N;j++)
//pour chaque
site
{
if (W3D) d=(Xx[j]-Vx[i])*(Xx[j]-Vx[i]) +(Xy[j]-Vy[i])*(Xy[j]-Vy[i])+(Xz[j]Vz[i])*(Xz[j]-Vz[i]) ;
//distance euclidienne
else d=(Xx[j]-Vx[i])*(Xx[j]-Vx[i]) +(Xy[j]-Vy[i])*(Xy[j]-Vy[i]);
d=1.0/d;
somme=0.0;
for (k=0;k<K;k++)
if (W3D) somme+=1.0/((Xx[j]-Vx[k])*(Xx[j]-Vx[k])+(Xy[j]-Vy[k])*(Xy[j]-Vy[k])
+(Xz[j]-Vz[k])*(Xz[j]-Vz[k] ));
else somme+=1.0/((Xx[j]-Vx[k])*(Xx[j]-Vx[k])+(Xy[j]-Vy[k])*(Xy[j]-Vy[k]));
if (Math.abs(u[i][j]-(d/somme))>maxu) maxu=Math.abs(u[i][j]-(d/somme));
u[i][j]=(double)(d/somme);
//degré
d'appartenance
}
somme=0;sommex=0;sommey=0;sommez=0;
for (j=0;j<N;j++)
{
somme+=u[i][j]*u[i][j];
sommex+=u[i][j]*u[i][j]*Xx[j];
sommey+=u[i][j]*u[i][j]*Xy[j];
if (W3D) sommez+=u[i][j]*u[i][j]*Xz[j];
}
Vx[i]=(double)(sommex/somme);
Vy[i]=(double)(sommey/somme);
if (W3D) Vz[i]=(double)(sommez/somme); else Vz[i]=minz;
}
} while (maxu>0.001);
//le maxi des
uij est < à epsilone
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
/**
* Calcule la distance exponentielle entre un site et un centroide
*/
private double de (int xi,int xj,Matrix F,Matrix F_inv,double P,double det)
//xi:points - xj:clusters
{
int i,j,t;
Matrix m,d,d_t;
double somme,de;
if (W3D) d=new Matrix(1,3);
else d=new Matrix(1,2);
somme=0;
// for (i=0;i<N;i++) somme+=u[xj][i];
//double P=somme/N;
jjaavvaa
- - 38 - -
ssoouurrccee jjaavvaa
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);
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
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;
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
System.err.println("expcluster");
for (int r=0;r<k;r++)
{
if (W3D) {
F[r] = new Matrix(3, 3);
F_inv[r] = new Matrix(3, 3);
}
else {
F[r] = new Matrix(2, 2);
F_inv[r] = new Matrix(2, 2);
}
}
if (W3D) d=new Matrix(1,3);
else d=new Matrix(1,2);
K=k;
//
for (i=0;i<K;i++)
cluster
{//CALCUL DE TOUTES LES MATRICES DE COV
//F=new Matrix(2,2);
//calcul de la matrice de cov floue du ième cluster
oouurrccee jjaavvaa
somme = 0;
for (j = 0; j < N; j++)
- - 39 - -
//pour chaque
{
ssoouurrccee jjaavvaa
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);
F[i] = F[i].div(somme);
det[i] = F[i].det();
F_inv[i] = F[i].inv();
//probabilité de sélectionner le ième cluster
//fin calcul matrice de cov
ssoouurrccee jjaavvaa
}
iter=0;
while (maxu>0.001 && iter<1000)
{
iter++;
maxu=0;
for (i=0;i<K;i++)
cluster
{
for (j = 0; j < N; j++)
site
{
somme = 0;
for (k = 0; k < K; k++)
{
dist=de(j, k, F[k], F_inv[k], P[k], det[k]);
if (k==i) dist1=dist;
somme += 1.0 / dist;
}
//pour chaque
//pour chaque
}
}
for (i=0;i<K;i++)
//pour chaque
cluster
{
somme = 0;
sommex = 0;
sommey = 0;
sommez = 0;
for (j = 0; j < N; j++)
{
somme += u[i][j] * u[i][j];
sommex += u[i][j] * u[i][j] * Xx[j];
sommey += u[i][j] * u[i][j] * Xy[j];
if (W3D) sommez += u[i][j] * u[i][j] * Xz[j];
}
Vx[i] = (double)(sommex / somme);
Vy[i] = (double)(sommey / somme);
if (W3D) Vz[i] = (double)(sommez / somme); else Vz[i]=minz;
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
double newu=(double)(1.0 / dist1 / somme);
if (Math.abs(u[i][j]-newu)>maxu) maxu=u[i][j]-newu;
u[i][j]=(double)newu;
//degré d'appartenance
ssoouurrccee jjaavvaa
//calcul de la matrice de cov floue du ième cluster
somme = 0;
for (j = 0; j < N; j++)
{
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
urrccee jjaavvaa
F[i] = F[i].div(somme);
- - 40 - -
det[i] = F[i].det();
F_inv[i] = F[i].inv();
//fin calcul matrice de cov
ssoouurrccee jjaavvaa
}
}
//le maxi des uij est < à epsilone
System.err.println("fin expcluster");
}
ssoouurrccee jjaavvaa
/**
* 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;
de la mat de cov
if (W3D) cov=new Matrix(3,3);
covariance floue 3D
else cov=new Matrix(2,2);
cov floue 2D
double somme;
double dpa=0;
//determinant
//matrice de
//matrice de
ssoouurrccee jjaavvaa
System.err.println("dpa");
if (W3D) d = new Matrix (1,3);
distance en 3D
else d = new Matrix (1,2);
en 2D
Matrix d_t;
Matrix m;
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
//distance
//transposé
for (j=0;j<K;j++)
{
for (i=0;i<N;i++)
{
d.set(0,0,Xx[i]-Vx[j]);
d.set(0,1,Xy[i]-Vy[j]);
distance
ssoouurrccee jjaavvaa
//vecteur
//vecteur
if (W3D) d.set(0,2,Xz[i]-Vz[j]);
d_t=d.trans();
m=d.mul(d_t);
m=m.mul(u[j][i]);
cov=cov.add(m);
//transposé
}
somme=0;
for (i=0;i<N;i++) somme+=u[j][i];
cov=cov.div(somme);
det=cov.det();
cov_inv=cov.inv();
somme=0;
for (i=0;i<N;i++)
{
d.set(0,0,Xx[i] - Vx[j]);
d.set(0,1,Xy[i] - Vy[j]);
if (W3D) d.set(0,2,Xz[i] - Vz[j]);
d_t=d.trans();
m=cov_inv.mul(d);
m=d_t.mul(m);
if (m.get()<1) somme+=u[j][i];
boule de rayon 1??
}
det=Math.sqrt(det);
- - 41 - -
//dans la
ssoouurrccee jjaavvaa
dpa+=somme/det;
moyenne de partition
}
dpa=dpa/K;
//densité
System.err.println("fin dpa");
return dpa;
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
/**
* 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<N;t++)
{
clusterize(t);
//exp_clusterize(t);
crit=dpa();
if (crit-dpa_avant<dpa_derive/2) break;
diminue?
dpa_derive=crit-dpa_avant;
if (t!=1) dpa_avant=crit;
}
return t-1;
}
}
- - 42 - -
//la dérivée
Matrix.java
/**
* 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<br>
* @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<cols;i++) for (j=0;j<rows;j++) m[i][j]=0;
}
/**
* Renvoi le coefficient colonne c,ligne r
* @param
c colonne<br>
* @param
r ligne
* @return coefficient c,r de la matrice
*/
public double get(int c,int r)
{
return m[c][r];
}
/**
* Renvoie le coéficient 0,0 de la matrice.Peut servir lorsque, à la suite
* d'une opération la matrice obtenue est de dimension 1,1 (un réel).
* @return coefficient 0,0
*/
public double get()
{
return m[0][0];
}
oouurrccee jjaavvaa
ssoouurrccee jjaavvaa
/**
* Constructeur par recopie...reserve l'espace mémoire pour la matrice
* @param cop matrice à copier
*/
public Matrix(Matrix cop)
{
int i,j;
rows=cop.rows;
cols=cop.cols;
m=new double [cols][rows];
for (i=0;i<cols;i++) for (j=0;j<rows;j++) m[i][j]=cop.m[i][j];
}
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
import java.awt.*;
import java.io.*;
- - 43 - -
ssoouurrccee jjaavvaa
/**
* Affecte le coefficient c,r de la matrice
* @param c colonne<br>
* @param r ligne<br>
* @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<cols;i++) for (int j=0;j<rows;j++) cov_add.m[i][j]=m[i][j]+m2.m[i][j];
return cov_add;
ssoouurrccee jjaavvaa
}
/**
* Divise une matrice par un réel
* @param
div réel divisant la matrice
* @return matrice résultante de la division
*/
public Matrix div(double div)
{
Matrix cov_div = new Matrix (cols,rows);
for (int i=0;i<rows;i++) for (int j=0;j<cols;j++) cov_div.m[i][j]=m[i][j]/div;
return cov_div;
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
/**
* Multiplie la matrice par une matrice m2 (attention,la taille
* de la matrice résultante est différente de celle de l'instance)
* @param
m2 matrice multiplicatrice
* @return matrice résultante de la multiplication
*/
public Matrix mul(Matrix m2)
{
int i,j,k;
double E1,E2,E3,E4,E5,E6,E7;
Matrix cov_mul = new Matrix (m2.cols,rows);
for (i=0;i<m2.cols;i++) for (j=0;j<rows;j++) for (k=0;k<m2.rows;k++)
cov_mul.m[i][j]+=m2.m[i][k]*m[k][j];
/*
E1=(m[1][0]-m[1][1])*(m2.get(0,1)+m2.get(1,1));
E2=(m[0][0]-m[1][1])*(m2.get(0,0)+m2.get(1,1));
E3=(m[0][0]-m[0][1])*(m2.get(0,0)+m2.get(1,0));
E4=(m[0][0]+m[1][0])*m2.get(1,1);
E5=m[0][0]*(m2.get(1,0)-m2.get(1,1));
E6=m[1][1]*(m2.get(0,1)-m2.get(0,0));
E7=(m[0][1]+m[1][1])*m2.get(0,0);
cov_mul.set(0,0,E1+E2-E4+E6);
cov_mul.set(1,0,E4+E5);
cov_mul.set(0,1,E6+E7);
cov_mul.set(1,1,E2-E3+E5-E7);
*/
return cov_mul;
urrccee jjaavvaa
}
- - 44 - -
ssoouurrccee jjaavvaa
/**
* 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<rows;i++) for (int j=0;j<cols;j++) cov_mul.m[i][j]=m[i][j]*mul;
return cov_mul;
}
/**
* Transpose la matrice (attention,les dimensions
* de la matrice résultante sont différentes de celles de l'instance)
* @return matrice transposée
*/
public Matrix trans()
{
Matrix m_trans= new Matrix(rows,cols);
ssoouurrccee jjaavvaa
for (int i=0;i<rows;i++) for (int j=0;j<cols;j++) m_trans.m[i][j]=m[j][i];
return m_trans;
}
/**
* Inverse une matrice.Attention,pas toujours possible!
* @return matrice inverse
*/
public Matrix inv()
{
Matrix ident=new Matrix (cols,rows);
double det=det();
ssoouurrccee jjaavvaa
/*
cov_inv.m[0][0]=(1.0f/det)*m[1][1];
cov_inv.m[1][0]=-(1.0f/det)*m[1][0];
cov_inv.m[0][1]=-(1.0f/det)*m[0][1];
cov_inv.m[1][1]=(1.0f/det)*m[0][0];
*/
if (det==0) System.err.println("Déterminant NUL!!!impossible d'inverser la matrice");
urrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
for ( int i = 0; i < cols; i++ )
ident.m[i][i] = 1.0;
d'une matrice identité
//création
int N = this.rows;
Matrix aa = new Matrix( this );
Matrix x = new Matrix( ident );
// descente; r+1 est le numero d'etape et de colonne à eliminer
for ( int r = 0; r < N; r++ ) {
// recherche du pivot
double pmax = Math.abs( aa.m[r][r] );
int imax = r;
for ( int i = r+1; i < N; i++ ) {
if ( Math.abs( aa.m[i][r] ) > pmax ) {
imax = i;
pmax = Math.abs( aa.m[i][r] );
}
}
// permutation des lignes de a et b
double tmp;
for ( int j = r; j < N; j++ ) {
tmp = aa.m[r][j];
aa.m[r][j] = aa.m[imax][j];
aa.m[imax][j] = tmp;
}
for ( int j = 0; j < x.cols; j++ ) {
tmp = x.m[r][j];
x.m[r][j] = x.m[imax][j];
- - 45 - -
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
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];
}
}
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
// 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<rows;i++)
{
ligne="";
for (int t=0;t<cols;t++) ligne=ligne+m[t][i]+"|";
System.err.println(ligne);
}
ssoouurrccee jjaavvaa
}
/**
* Retourne le déterminant de la matrice
* @return déterminant de la matrice
*/
public double det()
{
double det=0;
ssoouurrccee jjaavvaa
if (rows==2)
det=m[0][0]*m[1][1] - m[1][0]*m[0][1];
//2D
if (rows==3)
det=m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] +
m[0][2]*m[1][0]*m[2][1] - m[0][1]*m[1][0]*m[2][2] m[0][0]*m[1][2]*m[2][1] - m[0][2]*m[1][1]*m[2][0];
return det;
}
}
- - 46 - -
//3D
ssoouurrccee jjaavvaa
constants.java
interface constants
{
public static final
public static final
public static final
public static final
public static final
public static final
public static final
public static final
}
int
int
int
int
int
int
int
int
CENTROIDS=1;
UNCLASSIFIED=2;
BOX=3;
POINTS_SIZE=4;
X_FACTOR=5;
Y_FACTOR=6;
BG_COLOR=7;
THRESHOLD=8;
- - 47 - -
ssoouurrccee jjaavvaa
Cadre1.java
import
import
import
import
import
import
import
import
import
import
import
import
java.applet.Applet;
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.awt.geom.*;
java.awt.font.*;
java.io.*;
com.sun.j3d.utils.applet.MainFrame;
com.sun.j3d.utils.geometry.ColorCube;
com.sun.j3d.utils.universe.*;
com.sun.j3d.utils.behaviors.mouse.*;
javax.media.j3d.*;
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
/**
* 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);
l'environnement 3D
//créé
showStatus("INITIALISATION");
param = getParameter("nom");
//images du
oouurrccee jjaavvaa
site
- - 48 - -
ssoouurrcc
ssoouurrccee jjaavvaa
//est-ce un
Container content=this;
info= new Info();
content.add(info,"South");
barre=new Barre(bmp,this,v);
barre de boutons
content.add(barre,"North");
barre à la fenetre
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
upload = getParameter("upload");
upload?
//créé la
//ajoute la
repaint();
}
catch(Exception e) {
e.printStackTrace();
}
}
public void paint(Graphics g)
{
barre.image.repaint();
info.repaint();
showStatus("Fuzzy K means - SIP 2003 - " + bmp.titre);
}
}
- - 49 - -
urrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
Barre.java
import
import
import
import
import
import
import
import
import
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.awt.geom.*;
java.awt.font.*;
java.io.*;
com.sun.j3d.utils.applet.MainFrame;
com.sun.j3d.utils.geometry.ColorCube;
com.sun.j3d.utils.universe.*;
com.sun.j3d.utils.behaviors.mouse.*;
javax.media.j3d.*;
java.util.StringTokenizer;
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;
l'interface
final JComboBox aliste,bliste,sliste,nliste,pliste,fliste,tliste;
final JCheckBox casec,casea,caseb,casex,casey;
- - 50 - -
//les objets de
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
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);
panel à la Barre
this.add("South",bas);
this.add("Center",image);
//ajoute les
aliste = new JComboBox();
aliste.addItem("C-Moyennes Floues");
aliste.addItem("C-Moyennes Floues amélioré");
aliste.setSelectedIndex(0);
aliste.setMaximumSize(new Dimension(200,20));
gauche.add(aliste);
//type d'algo
bliste = new JComboBox();
for (int i = 1; i < 10; i++) bliste.addItem("de K=" + i);
bliste.setSelectedIndex(1);
bliste.setMaximumSize(new Dimension(80,20));
gauche.add(bliste);
//K de départ
tliste = new JComboBox();
for (int i = 1; i < 10; i++) tliste.addItem("à K=" + i);
tliste.setSelectedIndex(1);
tliste.setMaximumSize(new Dimension(80,20));
gauche.add(tliste);
//K d'arrivée
ssoouurrccee jjaavvaa
bfmle = new JButton("Continuer (k++)");
bfmle.setMaximumSize(new Dimension(150,20));
gauche.add(bfmle);
bfmle.setAlignmentX(0.5f);
sliste = new JComboBox();
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);
//seuil de
oouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
bkmeans = new JButton("GO");
bkmeans.setForeground(Color.red);
gauche.add(bkmeans);
bkmeans.setAlignmentX(0.5f);
nliste = new JComboBox();
if (framep.upload==null)
upload?
{
StringTokenizer st = new StringTokenizer( framep.param, "," );
sépare les noms passés en param
do {
nliste.addItem(st.nextToken());
chaque nom à la liste de fichiers
}
while (st.hasMoreTokens());
gauche.add(nliste);
nliste.setMaximumSize(new Dimension(200,20));
} else nliste.addItem(framep.upload);
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);
- - 51 - -
//c'est un
//non alors
//ajoute
//sinon,on
oouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
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)
le partitionnement (bouton GO)
{
double dpa_derive=0,dpa_avant=0,crit;
int t;
Graphics g2=framep.info.getGraphics();
//si on lance
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<=1+tliste.getSelectedIndex();t++)
//de .. à ...
{
bmpp.clusterize(t);
//partitionne
if (aliste.getSelectedIndex()==1) bmpp.exp_clusterize(t);
//si algo dist
exp
crit=bmpp.dpa();
//calcul le
DPA
System.err.println("recherche optimal: K="+t+" > DPA="+Math.round(10*crit)+" ;
dérivée:"+Math.round((crit-dpa_avant)*10));
String aff="";
for (int y=0;y<t;y++) aff=aff+"
Vx("+y+")="+Math.round(10*bmpp.Vx[y])+" - Vy("+y+")="+Math.round(10*bmpp.Vy[y])+" Vz("+y+")="+Math.round(10*bmpp.Vz[y])+"\r\n";
System.err.println(aff);
if ((crit-dpa_avant)<dpa_derive) break;
//la dérivée
diminue? alors on est allé trop loin
dpa_derive=crit-dpa_avant;
dpa_avant = crit;
}
t--;
bmpp.clusterize(t);
//on
partitionne en (t-1)
if (aliste.getSelectedIndex()==1)
{bmpp.exp_clusterize(t);bmpp.exp_clusterize(t);java.awt.Toolkit.getDefaultToolkit().beep();}
framep.repaint();
framep.info.repaint();
v.Create3dScene(bmpp);
framep.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
//remet le
curseur par défaut
//cligno.
- - 52 - -
soouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
}
if (e.getSource()==sliste)
de seuil
{
v.set_properties(THRESHOLD,sliste.getSelectedIndex()+1);
framep.repaint();
v.Create3dScene(bmpp);
}
//si on change
if (e.getSource()==nliste)
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();
}
//si on change
if (e.getSource()==casec)
l'affichage des centroides
{
v.set_properties(CENTROIDS,casec.isSelected());
framep.repaint();
v.Create3dScene(bmpp);
}
//on change
if (e.getSource()==casea)
des sites non affectés
{
v.set_properties(UNCLASSIFIED,casea.isSelected());
framep.repaint();
v.Create3dScene(bmpp);
}
//affichage
if (e.getSource()==pliste)
taille des points
{
v.set_properties(POINTS_SIZE,pliste.getSelectedIndex()+1);
framep.repaint();
v.Create3dScene(bmpp);
}
//change la
if (e.getSource()==bliste || e.getSource()==tliste)
modifie de ou à
{
if (bliste.getSelectedIndex()>tliste.getSelectedIndex())
{
tliste.setSelectedIndex(bliste.getSelectedIndex());
ai tjours de>à
repaint();
}
}
//si on
if (e.getSource()==caseb)
la boite?
{
v.set_properties(BOX,caseb.isSelected());
framep.repaint();
v.Create3dScene(bmpp);
}
//affichage de
- - 53 - -
//pour que on
if (e.getSource()==breset) v.reset();
//bouton reset
soouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
ssoouurrccee jjaavvaa
vue 3D
if ((e.getSource()==casex || e.getSource()==casey) && bmpp.W3D)
si le nuage est en 3D
{
if (casex.isSelected() && casey.isSelected()) v.set_factors(5,5);
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);
}
//uniquement
//gestion des
if (e.getSource()==fliste)
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);
}
//gestion du
if (e.getSource()==bfmle)
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);
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);
1er fichier au démarrage
}
//bouton
/**
* 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) ;
l'image
try {
InputStream is = file.openStream();
d'ouvrir l'image
} catch(java.io.IOException exp2) {nom="logosip.jpg";};
le fichier!
} catch(java.net.MalformedURLException exp) {};
prend le logo
- - 54 - -
//on ajoute
//charge le
//url de
//tente
//pas trouvé
//alors on
ssoouurrccee jjaavvaa
fim =framep.getImage(framep.getDocumentBase(),nom);
l'image (logo sip ou image stéréo)
while (fim.getHeight(framep)==-1) {};
chargement complet de l'image
}
}
- - 55 - -
//charge
//attends le
Téléchargement