Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Récupération des infos dans un fichier binaire C3D

22 réponses
Avatar
Michael
Bonsoir à tous,

pour un programme que je suis en train de développer, je dois extraire
des informations d'un fichier C3D écrit en binaire, et qui contient des
renseignements sur le résultat d'une capture de mouvement.

Le souci, c'est que je n'ai jamais eu à faire ce genre de lecture, et que
je sais pas comment m'y prendre.

Pour commencer, je voudrais juste pouvoir extraire les infos de l'entête
du fichier.
Dans la doc (http://www.c3d.org/HTML/default.htm), il y est dit que ce
fichier est composé de multiples blocs de 512 bytes.

//
All C3D files contain a minimum of three sections of information:
-----------------------------------------------------------------
A single, 512 byte, header section
-----------------------------------------------------------------
A parameter section consisting of one, or more, 512-byte blocks.
-----------------------------------------------------------------
3D point/analog data section consisting of one, or more, 512-byte blocks.
//

Cette page me donne les différentes composantes de cet entête:
http://www.c3d.org/HTML/description.htm

On m'a filé un code en C qui lit cet entête, mais j'y pipe rien du tout,
je vous la donne...

#define RECORD 512

typedef struct S_C3D {
int nbr_block_parameter;
int processor;
char **nom_block_parameter;
} C3D;

C3D c3d;

typedef struct matrice {
double **Mat;
} MATRICE;

typedef struct {
short cleffich; // w0 Clef du fichier
short nbmarqs; // w1 Nombre de marqueurs
short nbanalog; // w2 Nombre de donnees analogiques par image
video
short premimage; // w3 Premiere image
short dernimage; // w4 Derniere image
short maxinterpol; // w5 Interpolation maximum (nombre d'images)
float echelle; // w6-7 Facteur de conversion des donnees video
stockees sous forme d'entiers
short debutdonnees; // w8 Enregistrement de debut des donnees 1
enregistrement = 256 * word; 1 word = 16 bits
short freqanal; // w9 frequence d'echantillonage
analogique/donnees video
float freqvid; // w10-11 frequence d'echantillonage video
short x1[137]; // w12-w148 inutilise
short clef; // w149 mot clef (12345 octal typical value)
short nbevnttps; // w150 nb d'evenements temps definis
short x2; // w151 Number of defined time events
float fmtevnttps[18]; // w152-w187 dates evenements temporels (max
9-,pas 18!)
short fmtevntswitch[10];// w188-w197 byte switch de l evenement (0=on,1
=off)
char labelevnt[9][8]; // w198-w233 label de l evenement sur 4
caracteres
short x3[22]; // w234-w255 inutilise
}ENTETEC3D;

typedef union {
long lg;
float flt;
int in[2];
char ch[4];
}trans;

int LgLabel,NbTraj;
char *dim_label;
char **p_label; // Labels des points
trans tr;
short Nbimage;
char TempChar;

// Implémentation
float fltdecpc (char byt[4]) // fonction donnee par Oxford pour
transformer DEC en FLOAT .Recopiee integralement donc no comment !
{
long mantis;
short exp;
float flt;
float *f;

switch (proc) {
case 0:
f = (float *) byt;
flt = *f;
break;

case 1:
mantis = (byt[2] & 0xff) + ((byt[3] & 0xffl) << 8) + (((byt[0] &
0x7fl) | 0x80) << 16);
exp = (byt[1] & 0x7f) * 2 + ((byt[0] & 0x80) ? 1 : 0);
if (byt[1] & 0x80) mantis = -mantis;
flt = (mantis || exp) ? ((float) ldexp (mantis, (exp - 128 - 24)))
: 0;
break;
}
return (flt);
}


void litentete (char *nomfich)
{
char H[sizeof(ENTETEC3D)];
ENTETEC3D *header;
FILE *fich;
int i;

int n, G, m, n2, G2, d;
char *GroupName=NULL;
char *GroupDescription=NULL;
char *ParamName=NULL;
int *Dimensions=NULL;
short offset2, offset1;

if ((fich = OuvreFichier (nomfich, "rb", 0))!=NULL) {
header = (ENTETEC3D *)H;

for (i=0; i<sizeof(ENTETEC3D); i++) H[i] = fgetc(fich);

tr.flt = header->echelle;
header->echelle = fltdecpc(tr.ch);
tr.flt = header->freqvid;
header->freqvid = fltdecpc(tr.ch);
Nbimage = header->dernimage - header->premimage + 1;

// Lecture des paramètres pour récuperer les noms des marqueurs
correspondants à chaque trajectoire*/
fread (&TempChar, sizeof(char), 1, fich); // Byte 1
fread (&TempChar, sizeof(char), 1, fich); // Byte 1
fread (&TempChar, sizeof(char), 1, fich); // Byte 1
fread (&TempChar, sizeof(char), 1, fich); // Byte 1

// header
// n
fread (&TempChar, sizeof(char), 1, fich);
n = (int)TempChar;

while (n!=0) {
// G
fread (&TempChar, sizeof(char), 1, fich);
G = (int)TempChar;

// Group Name
GroupName = calloc (n+1, sizeof(*GroupName));
for (i=0;i<n;i++)
fread ((GroupName+i), sizeof(char), 1, fich);

// offset
fread (&offset1, sizeof(short), 1, fich);

// m
fread (&TempChar, sizeof(char), 1, fich);
m = (int)TempChar;

if (m==0) fseek(fich,(offset1-3),SEEK_CUR);
else {
GroupDescription = calloc (m, sizeof(*GroupDescription)); //
Group Description
for (i=0;i<(m);i++)
fread ((GroupDescription+i), sizeof(char), 1, fich);
free (GroupDescription);
}

// Paramètres
// n2
fread (&TempChar, sizeof(char), 1, fich);
n2 = (int) TempChar;

// G2
fread (&TempChar, sizeof(char), 1, fich);
G2 = (int) TempChar;

while (G2 == -G) {
// Param Name
ParamName = calloc (n2+1, sizeof(*ParamName));
for (i=0; i<n2; i++)
fread (&ParamName[i], sizeof(ParamName[i]), 1, fich);

// offset2
fread (&offset2, sizeof(short), 1, fich);

if ((strcmp(GroupName, "POINT")==0) &&
(strcmp(ParamName, "LABELS")==0)) {
// T
fread (&TempChar, sizeof(char), 1, fich);

// d
fread (&TempChar, sizeof(char), 1, fich);
d = (int) TempChar;

// Dimensions
Dimensions = calloc (d, sizeof(*Dimensions));
for (i=0;i<d;i++) {
fread (&TempChar, sizeof(char), 1, fich);
Dimensions[i] = (int)TempChar;
}

// lecture des labels
LgLabel = Dimensions[0];
NbTraj = Dimensions[1];
free (Dimensions);
fseek (fich, LgLabel*NbTraj, SEEK_CUR); // on passe les labels

// m2
fread(&TempChar, sizeof(char), 1, fich);
fseek(fich, (int)TempChar, SEEK_CUR); // on passe m2

fread (&TempChar, sizeof(char), 1, fich); // n2
n2 = (int)TempChar;

fread (&TempChar, sizeof(char), 1, fich); // G2
G2 = (int)TempChar;

}
else {
free (ParamName);

// Passage au parametre suivant
fseek(fich,(offset2-2),SEEK_CUR);

fread (&TempChar, sizeof(char), 1, fich); // n2
n2 = (int)TempChar;

fread (&TempChar, sizeof(char), 1, fich); // G2
G2 = (int)TempChar;
}
}

fseek (fich,(-2),SEEK_CUR);
free (GroupName);

fread(&TempChar, sizeof(char), 1, fich); // n
n = (int) TempChar;
}
FermeFichier (&fich);
}
}


Comment je peux lire correctement cet entête en bon C++?

Merci d'avance

Mike

10 réponses

1 2 3
Avatar
Etienne Rousee
"Michael" a écrit ...
Cette page me donne les différentes composantes de cet entête:
http://www.c3d.org/HTML/description.htm

On m'a filé un code en C qui lit cet entête, mais j'y pipe rien du tout,


A part modulariser un peu et remplacer les structures
par des classes et les calloc par des new, tu ne gagneras
pas grand chose à passer ce code en C++.
Il n'y a pas de façon simple de lire des données qui sont
structurellement compliquées, ce qui est presque
toujours le cas quand on tripatouille du binaire.
De plus, ce code ne me paraît pas si compliqué que ça,
sauf si tu as toi-même quelques problèmes en C.
Dans ce cas, extrais les lignes que tu ne comprends pas
et pose la question sur fclc.

--

Etienne

Avatar
Michael
"Etienne Rousee" wrote in news:43fd76f4$0$21278
$:


"Michael" a écrit ...
Cette page me donne les différentes composantes de cet entête:
http://www.c3d.org/HTML/description.htm

On m'a filé un code en C qui lit cet entête, mais j'y pipe rien du tout,


A part modulariser un peu et remplacer les structures
par des classes et les calloc par des new, tu ne gagneras
pas grand chose à passer ce code en C++.
Il n'y a pas de façon simple de lire des données qui sont
structurellement compliquées, ce qui est presque
toujours le cas quand on tripatouille du binaire.
De plus, ce code ne me paraît pas si compliqué que ça,
sauf si tu as toi-même quelques problèmes en C.
Dans ce cas, extrais les lignes que tu ne comprends pas
et pose la question sur fclc.



Ben en gros tout...

Sachant que le fichier fonctionne par blocs de 512 bytes, ya pes moyen
d'utiliser la STL pour lire bloc par bloc et de convertir chaque bloc en
une structure adéquate?

Du genre remplir un buffer de 512 bytes et transtyper en ENTETEC3D?


Avatar
kanze
Michael wrote:

pour un programme que je suis en train de développer, je dois
extraire des informations d'un fichier C3D écrit en binaire,
et qui contient des renseignements sur le résultat d'une
capture de mouvement.

Le souci, c'est que je n'ai jamais eu à faire ce genre de
lecture, et que je sais pas comment m'y prendre.


Tu as choisi un format assez difficile pour commencer:-).

Pour commencer, je voudrais juste pouvoir extraire les infos
de l'entête du fichier. Dans la doc
(http://www.c3d.org/HTML/default.htm), il y est dit que ce
fichier est composé de multiples blocs de 512 bytes.


Il y est aussi dit que les données sont écrites en binaire, avec
trois formats flottants différents et deux formats entiers. Ce
qui rend la vie nettement plus compliquée.

//
All C3D files contain a minimum of three sections of information:
-----------------------------------------------------------------
A single, 512 byte, header section
-----------------------------------------------------------------
A parameter section consisting of one, or more, 512-byte blocks.
-----------------------------------------------------------------
3D point/analog data section consisting of one, or more, 512-byte blocks.
//

Cette page me donne les différentes composantes de cet entête:
http://www.c3d.org/HTML/description.htm

On m'a filé un code en C qui lit cet entête, mais j'y pipe
rien du tout,


Pas grave -- il n'est pas correct. Il suppose beaucoup de choses
qui ne sont pas garanties (et souvent même pas vraies). Il
pourrait marcher sur certains fichiers, quand il a été compilé
avec certains compilateurs, sur certaines plate-formes, mais ce
n'est vraiment pas comme ça qu'il faut s'y prendre.

Lire l'en-tête, c'est déjà pas trivial, parce que l'en-tête
contient des entiers (16 bits) et des flottants (32 bits), et
qu'il faut lire le premier bloc des paramètres pour en savoir le
format. En gros, ce qu'il faut, c'est :

-- ouvrir le fichier (évidemment) ;
-- lire le premier octet, qui donne le début des paramètres ;
-- lire le deuxième octet, si ce n'est pas 0x50, on pourrait
s'arrêter tout de suite ;
-- se positionner au début des paramètres, c-à-d gseek( 512 *
premier octet lu, ios::beg ),
-- lire quatre octets,
-- selon le dernier de ces quatre octets, installer les
routines de lecture, et finalement
-- se positionner de nouveau au début, et commencer réelement.

Ça donne quelque chose du genre :

typedef unsigned char
uchar ;
typedef unsigned short
ushort ;

std::ifstream file( filename, std::ios::binary ) ;
if ( ! file ) {
// Erreur...
}
char soh[ 2 ] ;
file.read( soh, 2 ) ;
if ( ! file ) {
// Erreur ...
}
if ( soh[ 1 ] != 0x50 ) {
// Erreur...
}
if ( ! file.gseek( 512 * static_cast< uchar >( soh[ 0 ] ),
std::ios::beg ) ) {
// Erreur (fichier trop court ?)...
}
char sop[ 4 ] ;
file.read( sop, 4 ) ;
if ( ! file ) {
// Erreur...
}
switch ( sop[ 3 ] ) {
case 84 :
dataHandler = &intelDataHandler ;
break ;

case 85 :
dataHandler = &decDataHandler ;
break ;

case 86 :
dataHandler = &mipsDataHandler ;
break ;
}
file.gseek( 0, std::ios::beg ) ;

Au fond, je verrais bien une classe C3DFile, avec le code
ci-dessus dans le constructeur. Et avec une classe abstraite
DataHandler :

class DataHandler
{
public:
virtual ~DataHandler() {}
virtual short readShort( istream& source ) const = 0 ;
virtual float readFloat( istream& source ) const = 0 ;
} ;

Ce sont les instances concrètes de cette classe où ça se corse.
Pour un IntelDataHandler, par exemple, ça serait quelque chose
comme :

short
IntelDataHandler::readShort(
istream& source ) const
{
unsigned char tmp[ 2 ] ;
if ( ! source.read( reinterpret_cast< char* >( tmp ), 2 ) ) {
// Erreur...
}
// Je suppose ici que la machine cible utilise une
// représentation complément à deux, et gère la
// conversion unsigned -> signed de la façon classique.
return (tmp[ 1 ] << 8) | tmp[ 0 ] ;
}

float
IntelDataHandler::readFloat(
istream& source ) const
{
unsigned char tmp[ 4 ] ;
if ( ! source.read( reinterpret_cast< char* >( tmp ), 4) ) {
// Erreur...
}
float result
= ((tmp[ 2 ] & 0x7F) << 16) | (tmp[ 1 ] << 8) | tmp[ 0 ] ;
int dummy ;
result = ldexp( frexp( result, &dummy ),
tmp[ 2 ] >> 7 | tmp[ 3 ] << 1 ) ;
return (tmp[ 3 ] & 0x80) != 0
? - result
: result ;
}

Ensuite, pour chaque bloc, tu lis les données dans l'ordre, au
moyen des functions ci-dessus. Si on imagine une classe
C3DEntete, par exmple, qui resemble un peu à :

class C3DHeader
{
public:
//! pre
//! source est positionnée au début du fichier.
C3DInputFile& read( C3DInputFile& source ) ;
// ...

private:
long parameterOffset ;
int pointCount ;
int measurementCount ;
// ...
float frameRate ;
// ...
} ;

C3DInputFile&
C3DHeader::read(
C3DInputFile& source )
{
parameterOffset = 512 * source.readByte() ;
source.readByte() ; // sauter le 0x50.
pointCount = source.readShort() ;
measurementCount = source.readShort() ;
// ...
frameRate = source.readFloat() ;
// ...
}

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Michael
Tu as choisi un format assez difficile pour commencer:-).


Ben c'est à dire que là, je n'ai pas trop eu le choix ;)

Pas grave -- il n'est pas correct. Il suppose beaucoup de choses
qui ne sont pas garanties (et souvent même pas vraies). Il
pourrait marcher sur certains fichiers, quand il a été compilé
avec certains compilateurs, sur certaines plate-formes, mais ce
n'est vraiment pas comme ça qu'il faut s'y prendre.


Bon, ben c'est déjà bon à savoir ça!

Lire l'en-tête, c'est déjà pas trivial, parce que l'en-tête
contient des entiers (16 bits) et des flottants (32 bits), et
.......


Merci beaucoup!

Je me penche là dessus cet après-midi, et je reviens en cas de souci...
(autant dire à bientôt ;) )

Avatar
James Kanze
Michael wrote:
"Etienne Rousee" wrote in news:43fd76f4$0$21278
$:


"Michael" a écrit ...
Cette page me donne les différentes composantes de cet entête:
http://www.c3d.org/HTML/description.htm




On m'a filé un code en C qui lit cet entête, mais j'y pipe
rien du tout,




A part modulariser un peu et remplacer les structures par des
classes et les calloc par des new, tu ne gagneras pas grand
chose à passer ce code en C++. Il n'y a pas de façon simple
de lire des données qui sont structurellement compliquées, ce
qui est presque toujours le cas quand on tripatouille du
binaire. De plus, ce code ne me paraît pas si compliqué que
ça, sauf si tu as toi-même quelques problèmes en C. Dans ce
cas, extrais les lignes que tu ne comprends pas et pose la
question sur fclc.



Ben en gros tout...


Sachant que le fichier fonctionne par blocs de 512 bytes, ya
pes moyen d'utiliser la STL pour lire bloc par bloc et de
convertir chaque bloc en une structure adéquate?


Du genre remplir un buffer de 512 bytes et transtyper en ENTETEC3D?


Lire par blocs de 512 octets, ce n'est pas difficile -- voir
std::istream::read(). Mais il n'y a pas de conversion implicite
de char[512] vers ENTETEC3D. Cette partie-là, il va falloir que
tu le tappes toi-même.

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34



Avatar
Fabien LE LEZ
On 22 Feb 2006 22:57:31 GMT, Michael
:

On m'a filé un code en C qui lit cet entête, mais j'y pipe rien du tout


Moi, j'ai reçu un code C pour créer un hash MD5 à partir d'un char*.
Je n'ai pas cherché à le comprendre ; je me suis contenté de
l'encapsuler, de telle sorte que je n'aie pas à l'utiliser
directement.

J'ai maintenant un joli .h qui contient grosso modo :

std::string CalculerMD5 (std::string const&)

ainsi que quelques .c et .cpp qui font ce qu'ils veulent dans leur
coin.

Je te conseille de faire pareil : si le code fonctionne, inutile de le
refaire. Contente-toi de faire une jolie interface C++ pour l'utiliser
sans te prendre la tête.

Avatar
kanze
Fabien LE LEZ wrote:
On 22 Feb 2006 22:57:31 GMT, Michael
:

On m'a filé un code en C qui lit cet entête, mais j'y pipe
rien du tout


Moi, j'ai reçu un code C pour créer un hash MD5 à partir d'un
char*. Je n'ai pas cherché à le comprendre ; je me suis
contenté de l'encapsuler, de telle sorte que je n'aie pas à
l'utiliser directement.

J'ai maintenant un joli .h qui contient grosso modo :

std::string CalculerMD5 (std::string const&)

ainsi que quelques .c et .cpp qui font ce qu'ils veulent dans
leur coin.

Je te conseille de faire pareil : si le code fonctionne,
inutile de le refaire. Contente-toi de faire une jolie
interface C++ pour l'utiliser sans te prendre la tête.


Et comment est-ce que tu sais qu'il fonctionne, si tu ne le
comprends pas ? Dans le cas de MD5, il y a un fonctionnement
très précis et limité -- en fait, s'il calcule cinq ou six
hachages correctement, il y a de bonnes chances (prèsqu'une
certitude, en fait) qu'il est correct, et un survol rapide pour
vérifier qu'il n'y a pas de dépassement de buffer et des choses
de ce genre pourrait suffire. Sur ta machine, en tout cas --
passer sur une autre machine, et tu dois récommencer tous les
tests. Tandis que si tu jettes un coup d'oeil à son protocol...
tu verras que c'est plein d'options et de variantes. Le fait
d'avoir pu lire un fichier correctement, sur une machine, ne
prouve pas grand chose. Et en fait, un survol rapide m'a montré
qu'en effet, il ne traite pas les variantes ; il marchera sur
certains fichiers -- surtout ceux écrit sur la même machine --
et pas pour d'autres.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Fabien LE LEZ
On 24 Feb 2006 00:09:10 -0800, "kanze" :

Et comment est-ce que tu sais qu'il fonctionne, si tu ne le
comprends pas ?


Tout est dans le "on" de "On m'a filé un code[...]".

Si tu estimes que le "on" est digne de confiance, tu fais confiance au
code.
Sinon, si tu ne comprends pas le code, tu le jettes et tu le refais.

Avatar
Michael
Tout est dans le "on" de "On m'a filé un code[...]".

Si tu estimes que le "on" est digne de confiance, tu fais confiance au
code.
Sinon, si tu ne comprends pas le code, tu le jettes et tu le refais.


Tout est dit ;) Je refais :D

Avatar
Michael
class DataHandler
{
public:
virtual ~DataHandler() {}
virtual short readShort( istream& source ) const = 0 ;
virtual float readFloat( istream& source ) const = 0 ;
} ;

Ce sont les instances concrètes de cette classe où ça se corse.
Pour un IntelDataHandler, par exemple, ça serait quelque chose
comme :

short
IntelDataHandler::readShort(
istream& source ) const
{
unsigned char tmp[ 2 ] ;
if ( ! source.read( reinterpret_cast< char* >( tmp ), 2 ) ) {
// Erreur...
}
// Je suppose ici que la machine cible utilise une
// représentation complément à deux, et gère la
// conversion unsigned -> signed de la façon classique.
return (tmp[ 1 ] << 8) | tmp[ 0 ] ;
}

float
IntelDataHandler::readFloat(
istream& source ) const
{
unsigned char tmp[ 4 ] ;
if ( ! source.read( reinterpret_cast< char* >( tmp ), 4) ) {
// Erreur...
}
float result
= ((tmp[ 2 ] & 0x7F) << 16) | (tmp[ 1 ] << 8) | tmp[ 0 ] ;
int dummy ;
result = ldexp( frexp( result, &dummy ),
tmp[ 2 ] >> 7 | tmp[ 3 ] << 1 ) ;
return (tmp[ 3 ] & 0x80) != 0
? - result
: result ;
}


Quelles seraient les implémentations des classes permettant de lire des short
ou des floats pour des représentations DEC ou MIPS?

Je ne connais absolument rien à ce pan de la programmation...

1 2 3