Notes de programmation sur le programme LDI43.c et sur ses librairies



Ces quelques notes correspondaient a une version precedente (ldi43). Elles sont a peu pres toutes d actualite pour la version ldi84, meme si il aurait fallu rajouter des fonctions. Seule la premiere partie est en HTML. La suite est en texte, par manque de temps.






1.Librairie de manipulation des matrices/Vecteurs: mat43.h

Cette librarie ne sert qu aux calcul dans l espace 3D, et donc n agit que sur des vecteurs de dimension 3, ou des matrices 3*3,definies dans un typedef comme des `Vecteur3` et des `Matrice33`, respectivement des tableaux de 3 elements et des matrices de 3 * 3 elements. Il y a toute les fonctions habituelles:

void CopieMatrice(Matrice33 Source,Matrice33 Destination) fait que la matrice Destination prend la valeur de celle Source.

void CopieVecteur(Vecteur3 Source, Vecteur3 Destination) est son equivalent pour les vecteurs.

void CreerMatrice(float m11,float m12,float m13,float m21,float m22,float m23, float m31,float m32,float m33,Matrice33 C) est une fonction pour remplir la matrice C. A la sortie, on a ligne i, colonne j, C= mij.

void CreerVecteur(float v1,float v2,float v3,Vecteur3 V) est son equivalent pour les vecteurs pour remplir le vecteur V.

float NormeV(Vecteur3 V) renvoie la norme euclidienne d un vecteur.

void Vectoriel(Vecteur3 U,Vecteur3 V,Vecteur3 W) est fait pour calculer le produit scalaire: a la sortie, on a W=U^V.

float Scalaire(Vecteur3 U,Vecteur3 V) renvoie le produit scalaire de U par V.

float cosinus(Vecteur3 U,Vecteur3 V) renvoie le cosinus de l angle (au sens de l angle 3D) entre les vecteurs U et V.

void ProduitMM(Matrice33 A,Matrice33 B,Matrice33 C) est la fonction pour calculer le produit matriciel. A la sortie, on a C qui a pris la valeur AB.

void ProduitMV(Matrice33 A,Vecteur3 U,Vecteur3 V) est la fonction pour calculer le produit d une matrice par un vecteur. A la sortie, on a V=AU.

void ProduitMS(Matrice33 A,float scalaire,Matrice33 C) est la fonction qui permet de multiplier une matrice par un scalaire. A la sortie, on a C=scalaire*A.

void ProduitVS(Vecteur3 U,float scalaire,Vecteur3 V) est la fonction similaire pour les vecteurs. A la sortie, on a V=scalaire*U.

void SoustractionV(Vecteur3 U,Vecteur3 V,Vecteur3 W) est la fonction qui permet de faire la soustraction entre 2 vecteurs. A la sortie, on a W=U-V.

void AdditionV(Vecteur3 U,Vecteur3 V,Vecteur3 W) fait elle l addition des 2 vecteurs. A la sortie, on a W=U+V.

float Determinant(Matrice33 A) calcule le determinant d une matrice.

void VoirMatrice(Matrice33 Mat) est une fonction qui permet de visualiser une matrice. Elle a surtout une utilite pour les debuggages, et je l ai donc laisse pour une eventuelle utilisation.

void VoirVecteur(Vecteur3 a) est la meme fonction pour les vecteurs.

void InverseMatrice(Matrice33 A,Matrice33 C ) est la fonction qui permet, si cela est possible, c est a dire si le determinant n est pas nul, d inverser une matrice. Ainsi, si det(A)<>0, on a, a la sortie, C=inv(A). Sinon, on a un message indiquant le probleme, et C ne change pas de valeur.

Note: Cette librairie pourrait etre augmentee, par des fonctions du type AdditionnerM,SoustraireM, additions et soustractions sur matrices, ou par des fonctions transposees. Mais je n ai pris que l indispensable pour le programme.




2.Librairie des differents types de donnees: types43.h

Outre les types Vecteur3 et Matrice33, definis dans mat43.h, nous avons les types suivants:

Notation: j utilise souvent des types du genre pA, qui sont des pointeurs sur des variables de type A. Cela permet de ne pas avoir de `*` partout dans le programme, notamment lors de l utilisation des structures.

Les types immediats sont Couleur (entier), Image (unsigned char *), qui est en fait un tableau de unsigned char,TabProfondeur (float*), qui est un tableau representant la carte des profondeurs. TableauSplatting est un type qui ne me sert plus et qui devrait disparaitre dans une version future, et Flag (int) est le type qui permet de savoir quand sortir de boucles ou si l on est passe par un endroit du programme.

DepthPixel est une structure composee de 3 unsigned char, Rouge, Vert et Bleu, qui represente la couleur, et d un float Distance qui represente la distance du DepthPixel a l observateur.

pDepthPixel est un pointeur sur un DepthPixel.

LayeyredDepthPixel est une structure composee d un entier, NombreLayer, et d un pointeur sur un pDepthPixel. Il a la signification suivante: NombreLayer est le nombre d elements que contient le tableau de pDepthPixel donne par Layer. Ainsi, Layer est un tableau qui permet d acceder aux differents DepthPixel qui compose le LayeredDEpthPixel.

pLayeredDepthPixel est un pointeur sur un LayeredDepthPixel.

ParametreCamera est une structure qui comporte les parametres de calibrage de la camera: les Vecteur3 a,b,c, le float Focale et le point de vue From, qui est un Vecteur3.

pParametreCamera est un pointeur sur un ParametreCamera.

Enfin, nous avons le type LayeredDepthImage (LDI en abrege), compose de 2 entiers xres et yres donnant la resolution en x et en y, d un pParametreCamera donnant le calibrage de la LDI, et d un pointeur sur un pLayeredDepthPixel Pixels. En fait, Pixel est un tableau de xres*yres elements, elements qui sont des pointeurs sur les LayeredDepthPixel. Ainsi, Pixel[j*xres+i] represente le pLayeredDepthPixel du pixel de l ecran P(i en abscisse, j en ordonnee).

pLayeredDepthImage est un pointeur sur un LayeredDepthImage.

La derniere structure, DonneesSource, est plus une structure due a l implementation qu a la theorie. Elle sert a lire des fichiers source enregistres, fichiers .source qui contiennent les donnees suivantes: nombre d images utilisees pour creer le LDI, et nom de celles ci. Alors, TableauImage sera le tableau des images (lues par les fichiers .ppm), TableauProfondeur sera le tableau des cartes de profondeur, et TableaupCamera sera le tableau des Parametres de camera de chacune des images de reference.

pDonneesSource est un pointeur sur DonnesSource.




3. Librairie des lectures/ecritures de fichiers: fichiers43.h



void EnregistrerImage (char* nom,Image image,int xres,int yres) enregistre l Image image dans le fichier [nom]+".ppm".

void LireCam(char* nom,Vecteur3 a,Vecteur3 b,Vecteur3 c,Vecteur3 From, Matrice33 P,int* pxres,int* pyres, float* pfocale) lit le fichier [nom]+".cam" et met les valeurs qu il a lu dans ce fichier, ou deduit, dans les Vecteur3 a, b, c, From, dans la Matrice33 P, dans les entiers xres et yres et dans le float focale, passes par parametres. Si le fichier n est pas lu, une erreur est declenchee.

unsigned char * LireImage (char* nom,Vecteur3 a,Vecteur3 b,Vecteur3 c,Vecteur3 From, int* pxres,int* pyres,Matrice33 P,float* pfocale) est une fonction qui lit la camera [nom]+".cam", et donc change a,b,c,From,P,xres,yres et focale, et qui lit l image [nom]+".ppm". Elle renvoie alors un unsigned char*, c est a dire un Image, qui est l image lue.

void EnregistrerCam (char* nom,Vecteur3 a,Vecteur3 b,Vecteur3 c,Vecteur3 From, int xres,int yres,float focale) enregistre la camera dans le fichier [nom]+".cam".

float * LireProfondeur(char * nom,int xres,int yres) est une procedure qui lit un fichier [nom]+".z", et renvoie le float*, c est a dire le TabProfondeur donnant ce qui a ete lu.

void VoirProfondeur (char* nom,TabProfondeur profondeur,int xres,int yres) est une fonction qui transforme le tableau des prondeurs en un fichier noir et blanc [nom]+".pgm". Cela permet de verifier les fichiers .z que l on recoit, et notamment s ils ne sont pas la transposee, ou l envers des fichiers que l on voudrait avoir. La fonction est donc surtout utilise pour le debuggage.

void EnregistrerProfondeur(char* nom,TabProfondeur profondeur,int xres,int yres) enregistre la carte des profondeur pour la restaurer ensuite ou l exporter, dans le fichier [nom]+".z". On ne peut donc la confondre avec VoirProfondeur.

void VoirSplat(char* nom, TableauSplatting tsplat,int xres,int yres) est une fonction qui ne me sert plus et qui devrait donc disparaitre.

void CreerFichierSource() permet de creer un fichier .source. Voir paragraphe 2, le type DonneesSource.

pDonneesSource CreerDonneesSources(Image* TableauImage,TabProfondeur* TableauProfondeur,pParametreCamera* TableaupCamera,int NbImage) permet de creer une structure DonneesSource. C est l equivalent des CreerVecteur et CreerMatrice de mat43.h.

void DetruireDonneesSource (pDonneesSource pLectureDonnees) libere la place prise par la structure pLectureDonnees.

pDonneesSource LireFichierSource (char * nom,int* pxres,int* pyres) lit un fichier .source, et met la valeur de xres et yres a celle qu il a lu.




4.Fin du rapport en texte

Suite du rapport en texte




5.Ce qui ne marche pas et qu il serait bon de corriger


Tout d abord, je voudrais attirer l attention sur le fait que, pour des raisons de simplicite, et pour eviter trop de tests, les differents warping n operent pratiquement jamais sur les bords de l image (parce que au milieu de l image, je fais des Splatting 3*3, et qu au bord, pour ne pas faire de segmentation fault, je devrais faire des Splatting 1*1). Cela pourrait se changer au prix de tests ralentisseurs supplementaires. Ou alors on pourrait faire une fonction qui lisse le bord, lissage qui ne se verrait sans doute pas du tout. La troisieme solution est en fait de considerer l image obtenue comme une image de resolution un tout petit peu moindre, en ne prenant que l image moins son bord.

Un probleme plus important est celui du tri des IBO, tri qui ne fonctionne pas. Ce tri est lance par un appel du type:TrierListeSuivantAlphaBeta (TableauNomIBO,TableauTransf,NombreIBO,QuelPlan,Fromd,TableauIndiceIBOTrie). Ici, TableauNomIBO est le tableau qui contient les noms des IBO, directement lu dans le .sceneIBO, TableauTransf contient les rotations/translations lus dans ce meme .sceneIBO, NombreIBO est le nombre d IBO, QuelPlan est le vecteur normal au plan sur lequel on va projeter les points 3D, Fromd est le point de vue et TableauIndiceTrie est le tableau dans lequel la fonction va mettre l ordre des indices dans lequel il faut projeter (et non pas les noms, car il peut y avoir plusieurs fois le meme nom d IBO, comme dans le cas d une foret).

Cette fonction appelle une fonction recursive:TrierListeSuivantAlphaBeta_Separation (TableauNomIBO,TableauTransf,ListeIndice,NombreIBO,QuelPlan,Fromd,TableauIndiceIBOTrie,0), ou ListeIndice est un tableau des NombreIBO premiers elements de l ensemble des entiers naturels. Cette fonction appelee sert a separer la liste d indice qui lui est donnee en L11,L12,L21 et L22 selon ce qui est explique dans l explication theorique des IBO. Cela se fait en ayant d abord calcule l hypothetique centre, et les differents alpha/beta. Notons qu il faudrait peut etre se preoccuper du fait que l hypothetique centre appartient a un cube ou non, car l article propose alors de deplacer l hypothetique centre.

Une fois ces 4 listes faites, il ne reste plus qu a appeler void TrierListeSuivantAlphaBeta_Concatenation (pTripletFloat* ListeTripletFloat,int LongueurListe,int offset,int* TableauTrieSuivantAlpha,char** TableauNomIBO,float* TableauTransf,Vecteur3 QuelPlan,Vecteur3 Fromd), qui est la fonction chargee de voir des eventuels chevauchements (alpha[i+1]<beta[i] en valeur absolue), et qui alors relance une division de la liste constitueee par les indices des IBO qui se chevauchent. ListeTripletFloat est une liste contenant des triplets (indice, alpha(indice),beta(indice)), LongueurListe est la longueur de cette liste, offset est une fonction de memoire qui permet, malgre les appels recursifs de remettre les donnees au bon endroit dans la liste TableauTrieSuivantAlpha, liste d indice dont le calcul est le but des fonctions et qui contiendra l ordre de projection.

En fait, sur ces 3 fonctions, je ne suis sur d aucune. Je n ai pas vraiment eu le temps de faire des tests approfondis: j ai quand meme laisse un grand nombre de printf, pour voir ce qui se passait quand ca ne marche pas. Il faudrait je pense verifier les alpha/beta qui sont calcules sur le premier passage. Ensuite, il sera simple de verifier la fabrication des 4 listes. A ce moment la, tout probleme ne pourra venir que de TrierListeSuivantAlphaBeta_Concatenation.

De plus, lorsque j ai fait les IBO, j ai compris qu il y avait un ordre pour projeter les differents DP d un LDP: cet ordre a ete installe dans le warping des IBO comme suit.
          CreerVecteur(u,v,1,xr);
          ProduitMV(Pr,xr,RayonR);
          
          if (cosinus(vdir,RayonR)>0) {   //si on est du meme sens de point de 
                                          //vue, on parcourt des DP les plus 
                                          //profonds au plus proches

             if ((i<xres-1) && (j<yres-1)) {   
  

                for (k=NombreLayer-1;k>-1;k--) {   
                   pDP=(pLDP->Layer)[k];
                   profondeur=pDP->Distance;

                   profondeurD=Warping_Point_Simple(Pr,Pdd,Fromr,Fromd,xr,xd,
                      profondeur,vdir);    
                   uint=arrondi(xd[0]);
                   vint=arrondi(xd[1]);
         
                   if ((pDP->Rouge!=ROUGE_FOND) ||(pDP->Vert!=VERT_FOND) 
	              ||(pDP->Bleu!=BLEU_FOND)) {  	

                       if (profondeurD>0){
                          if ((uint>=1) && (vint>=1) && (uintxj<res-1) && (vintj<yres-1)) {
                             SplatterSimple(imaged,xres,uint,vint,
                             pDP->Rouge,pDP->Vert,pDP->Bleu,1); 
                          }    
                       }
                   }  
                }
             }
          } else {                         //sinon, des plus proches au plus 
                                           //profonds
            if ((i<xres-1) && (j<yres-1)) {   
               for (k=0;k<NombreLayer;k++) {  
                 pDP=(pLDP->Layer)[k];
                 profondeur=pDP->Distance;

                 profondeurD=Warping_Point_Simple(Pr,Pdd,Fromr,Fromd,xr,xd,
                      profondeur,vdir);    
                 uint=arrondi(xd[0]);
                 vint=arrondi(xd[1]);
         
                 if ((pDP->Rouge!=ROUGE_FOND) ||(pDP->Vert!=VERT_FOND) 
	            ||(pDP->Bleu!=BLEU_FOND)) {  	

                   if (profondeurD>0){
                     if ((uint>=1) && (vint>=1) && (uintxj<res-1) && (vintj<yres-1)) {
                        SplatterSimple(imaged,xres,uint,vint,
                        pDP->Rouge,pDP->Vert,pDP->Bleu,1); 
                     }    
                   }
                 }  
               }
            }
          }       


En fait, il faudrait installer cet ordre pour les simples LDI. C est tres simple, les deux donctions etant quasiment les memes (copie colle), mais je ne l ai pas fait car je n aurai pas eu le temps de tester. Cela se fera tres facilement. Il faut le faire dans WarperLDI et WarperLDItelquel.






Benoit Chevallier

Last modified: Wed Aug 4 16:08:11 MET DST 1999