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

Lire une image PPM en binaire

1 réponse
Avatar
fanny Chevalier
--------------B22D45277ED13849767D90D1
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit

Bonjour,

Je croyais avoir resolu mon probleme quant a la lecture d'une image ppm
de type 'P6'
dont la structure est

P6
12
12
255
//données en binaire

Mon problème est le suivant :
ce morceau de code est valable pour quelques images, mais pas toutes


ifstream input (fileName.c_str ());
if (!input)
{
cerr << "Error : bad file format" << endl;
exit (EXIT_FAILURE);
}


char headerChar;
int headerInt;

input >> headerChar >> headerInt;

if (headerChar != 'P' || headerInt != 6)
{
cerr << "Error : bad file format, need 'P6' file" << endl;
exit (EXIT_FAILURE);
}

int width, height, maxIntensity;

input >> width >> height >> maxIntensity;

Image *image = new Image(width, height, maxIntensity);

// Colors recording
for (int i = 0 ; i < height ; i++)
for (int j = 0 ; j < width ; j++)
{
unsigned char red, green, blue;
input >> red >> green >> blue;
image->setColor(i, j, red, green, blue);
}

input.close();
return image;
}

En effet, sur la plupart des images, un décalage s'effectue dans la

lecture des données : un saut d'une valeur decale les informations

de couleurs car les valeurs de rouge passent au bleu, celle du vert

passent au rouge et celle du bleu au vert... Un autre saut entraine

d'autre decalages... C'est tres penible.

Une alternative consiste a ecrire :

ifstream input (fileName.c_str (), ios::binary);
if (!input)
{
cerr << "Error : bad file format" << endl;
exit (EXIT_FAILURE);
}

char headerChar;
char headerInt;

input.read(&headerChar, 1);
input.read(&headerInt, 1);

if (headerChar != 'P' || (headerInt != '6' && headerInt != '3'))
{
cerr << "Error : bad file format, need 'P6' file" << endl;
exit (EXIT_FAILURE);
}

int width, height, maxIntensity;
input >> width >> height >> maxIntensity;
//input.getline(0x0, 10);
Image *image = new Image(width, height, maxIntensity);

// Pour lire le \n qui fait decaler les couleurs...
char toto;
input.read(&toto, 1);


// Case 'P6' image
if (headerInt == '6')
{
// Colors recording
for (int i = 0 ; i < height ; i++)
for (int j = 0 ; j < width ; j++)
{
unsigned char red, green, blue;
input.read(&red, 1);
input.read(&green, 1);
input.read(&blue, 1);
image->setColor(i, j,
(unsigned char) red,
(unsigned char) green,
(unsigned char) blue);
if (i == 0 && j == 0)
cout << (int) red << " " << (int) green << " " << (int) blue << endl;

}

input.close();
return image;

Mais ce bout de code ne permet de lire que les images correspondant

à la configuration :

P6\n

width\n

height\n

maxIntensity\n <- ce caractère devient obligatoire et DOIT etre le seul avant le flux binaire

flux binaire sans parasites (pas de \n ou \t ...)

Quelqu'un a-til une routine de lecture de fichier 'P6' en c++ ?

Sinon, comment lire mon fichier correctement a partir du code

fournit?

Merci par avance...

--
Fanny



--------------B22D45277ED13849767D90D1
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit

<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
Bonjour,
<p>Je croyais avoir resolu mon probleme quant a la lecture d'une image
ppm de type 'P6'
<br>dont la structure est
<p>P6
<br>12
<br>12
<br>255
<br>//donn&eacute;es en binaire
<p>Mon probl&egrave;me est le suivant :
<br>ce morceau de code est valable pour quelques images, mais pas toutes
<br>&nbsp;
<p><i><font color="#009900">&nbsp; ifstream input (fileName.c_str ());</font></i>
<br><i><font color="#009900">&nbsp; if (!input)</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp; {</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cerr &lt;&lt;
"Error : bad file format" &lt;&lt; endl;</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (EXIT_FAILURE);</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp; }</font></i>
<br><i><font color="#009900"></font></i>&nbsp;<i><font color="#009900"></font></i>
<p><i><font color="#009900">&nbsp; char headerChar;</font></i>
<br><i><font color="#009900">&nbsp; int headerInt;</font></i><i><font color="#009900"></font></i>
<p><i><font color="#009900">&nbsp; input >> headerChar >> headerInt;</font></i><i><font color="#009900"></font></i>
<p><i><font color="#009900">&nbsp; if (headerChar != 'P' || headerInt !=
6)</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp; {</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cerr &lt;&lt;
"Error : bad file format, need 'P6' file" &lt;&lt; endl;</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (EXIT_FAILURE);</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp; }</font></i>
<br><i><font color="#009900">&nbsp;</font></i>
<br><i><font color="#009900">&nbsp; int width, height, maxIntensity;</font></i><i><font color="#009900"></font></i>
<p><i><font color="#009900">&nbsp; input >> width >> height >> maxIntensity;</font></i><i><font color="#009900"></font></i>
<p><i><font color="#009900">&nbsp; Image *image = new Image(width, height,
maxIntensity);</font></i><i><font color="#009900"></font></i>
<p><i><font color="#009900">&nbsp; // Colors recording</font></i>
<br><i><font color="#009900">&nbsp; for (int i = 0 ; i &lt; height ; i++)</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp; for (int j = 0 ; j &lt;
width ; j++)</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></i>
<br><i><font color="#009900">&nbsp;unsigned char red, green, blue;</font></i>
<br><i><font color="#009900">&nbsp;input >> red >> green >> blue;</font></i>
<br><i><font color="#009900">&nbsp;image->setColor(i, j, red, green, blue);</font></i>
<br><i><font color="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></i><i><font color="#009900"></font></i>
<p><i><font color="#009900">&nbsp; input.close();</font></i>
<br><i><font color="#009900">&nbsp; return image;</font></i>
<br><i><font color="#009900">}</font></i>
<pre></pre>

<pre>En effet, sur la plupart des images, un d&eacute;calage s'effectue dans la</pre>

<pre>lecture des donn&eacute;es : un saut d'une valeur decale les informations</pre>

<pre>de couleurs car les valeurs de rouge passent au bleu, celle du vert</pre>

<pre>passent au rouge et celle du bleu au vert... Un autre saut entraine</pre>

<pre>d'autre decalages... C'est tres penible.</pre>

<pre></pre>

<pre>Une alternative consiste a ecrire :</pre>

<pre></pre>

<pre>&nbsp;<i><font color="#009900"> ifstream input (fileName.c_str (), ios::binary);
&nbsp; if (!input)
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cerr &lt;&lt; "Error : bad file format" &lt;&lt; endl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (EXIT_FAILURE);
&nbsp;&nbsp;&nbsp; }


&nbsp; char headerChar;
&nbsp; char headerInt;

&nbsp; input.read(&amp;headerChar, 1);
&nbsp; input.read(&amp;headerInt, 1);

&nbsp; if (headerChar != 'P' || (headerInt != '6' &amp;&amp; headerInt != '3'))
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cerr &lt;&lt; "Error : bad file format, need 'P6' file" &lt;&lt; endl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (EXIT_FAILURE);
&nbsp;&nbsp;&nbsp; }
&nbsp;
&nbsp; int width, height, maxIntensity;
&nbsp; input >> width >> height >> maxIntensity;
&nbsp; //input.getline(0x0, 10);
&nbsp; Image *image = new Image(width, height, maxIntensity);

&nbsp; // Pour lire le \n qui fait decaler les couleurs...
&nbsp; char toto;
&nbsp; input.read(&amp;toto, 1);

&nbsp;&nbsp;
&nbsp; // Case 'P6' image
&nbsp; if (headerInt == '6')
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Colors recording
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0 ; i &lt; height ; i++)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0 ; j &lt; width ; j++)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char red, green, blue;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input.read(&amp;red, 1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input.read(&amp;green, 1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input.read(&amp;blue, 1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image->setColor(i, j,&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (unsigned char) red,&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (unsigned char) green,&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (unsigned char) blue);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i == 0 &amp;&amp; j == 0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; (int) red &lt;&lt; " " &lt;&lt; (int) green &lt;&lt; " " &lt;&lt; (int) blue &lt;&lt; endl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;
&nbsp; input.close();
&nbsp; return image;</font></i></pre>

<pre><i><font color="#009900"></font></i></pre>

<pre><font color="#000000">Mais ce bout de code ne permet de lire que les images correspondant</font></pre>

<pre><font color="#000000">&agrave; la configuration :</font></pre>

<pre><font color="#000000">P6\n</font></pre>

<pre><font color="#000000">width\n</font></pre>

<pre><font color="#000000">height\n</font></pre>

<pre><font color="#000000">maxIntensity</font><font color="#FF0000">\n&nbsp;&nbsp;&nbsp;&nbsp; &lt;- ce caract&egrave;re devient <b>obligatoire</b> et <b>DOIT</b> etre le seul avant le flux binaire</font></pre>

<pre>flux binaire sans parasites (pas de \n ou \t ...)</pre>

<pre></pre>

<pre></pre>

<pre></pre>

<pre>Quelqu'un a-til une routine de lecture de fichier 'P6' en c++ ?</pre>

<pre>Sinon, comment lire mon fichier correctement a partir du code</pre>

<pre>fournit?</pre>

<pre></pre>

<pre>Merci par avance...</pre>

<pre>--&nbsp;
Fanny&nbsp;
</pre>
&nbsp;</html>

--------------B22D45277ED13849767D90D1--

1 réponse

Avatar
kanze
fanny Chevalier wrote in message
news:...

Je croyais avoir resolu mon probleme quant a la lecture d'une image
ppm de type 'P6' dont la structure est

P6
12
12
255
//données en binaire

Mon problème est le suivant :

ce morceau de code est valable pour quelques images, mais pas toutes

ifstream input (fileName.c_str ());
if (!input)
{
cerr << "Error : bad file format" << endl;


Drôle de message d'erreur pour ce cas-ci. J'aurais imaginé quelque chose
plutôt du genre : « Cannot open file ».

Aussi, si le flux contient des données en binaire, il faut absolument
l'ouvrir en mode binaire. Donc :

std::ifstream input( fileName.c_str(), std::ios::binary ) ;

Sinon, tu risques d'avoir des problèmes pour certaines valeurs binaires,
dans certains contextes, selon la plateforme. (Sous Windows, par
exemple, un 13 suivi d'un 10 sera supprimé. Sur un Mac, je crois qu'un
13 serait changé en 10. Et ainsi de suite -- chaque plateforme a ses
propre règles.)

exit (EXIT_FAILURE);
}

char headerChar;
int headerInt;

input >> headerChar >> headerInt;


Je ne suis pas sûr ici. S'il s'agit réelement toujours d'exactement deux
caractères, j'aurais plutôt une tendance à lire deux caractères, avec
get(). Mais je ne connais pas le format réel ; c'est donc difficile à

- saute des blancs (espaces, tabs et fins de lignes),
- lit le premier non blanc en headerChar,
- saute de nouveau des blancs, et
- lit un chiffre avec une signe optionnelle suivie d'un ou plus de
chiffres dans headerInt.

Si le fichier commence par « P6 », pas de problème. Si le fichier
commence par « P +006 » non plus. Si tu veux exactement « P6 », et
rien d'autre, et les champs ci-dessus sont bien séparés par des fins de
ligne, j'écrirais plutôt :

std::string header ;
if ( ! std::getline( input, header ) || header != "P6" ) {
// Erreur de formattage du fichier...
}

Note en revanche que si tu ouvres le fichier en binaire, getline risque
de ne pas marcher sur certains systèmes (p.e. Mac), et donner de mauvais
résultat sur d'autres (Windows). Le mieux dans ce cas-ci, c'est
probablement d'écrire ton propre getline(), qui reconnaît la façon qui
sert à séparer les lignes dans ce type de fichier. Alternativement :

if ( input.get() != 'P' || input.get() != ´6' || ! skipNL( input ) ) {
// Erreur de formattage du fichier...
}

De toute façon, il faut que tu gères les fin de lignes exprès toi-même.
(Et celui qui a mélangé du binaire et du texte orienté ligne dans le
même fichier est un plutôt incompétent pour définir un format.) Ce que
fait skipNL dépend du format, mais si les caractères en entrée
correspond à une fin de ligne dans le format, il doit les extraire et
revoyer true ; sinon, il renvoie false.

if (headerChar != 'P' || headerInt != 6)
{
cerr << "Error : bad file format, need 'P6' file" << endl;
exit (EXIT_FAILURE);
}

int width, height, maxIntensity;

input >> width >> height >> maxIntensity;


Ici aussi, c'est une simplification, qui pourrait cacher certaines
erreurs de formattage. C'est peut-être acceptable, mais évidemment, il
va accepter les trois nombres même s'ils sont sur la même ligne.

Note aussi qu'il a extrait les trois nombres, ainsi que des espaces
blancs (y compris des fins de lignes) qui les précèdent chaque fois,
mais que le ou les caractères de fin de lignes qui suivent le dernier
nombre sont toujours là.

Image *image = new Image(width, height, maxIntensity);

// Colors recording
for (int i = 0 ; i < height ; i++)
for (int j = 0 ; j < width ; j++)
{
unsigned char red, green, blue;
input >> red >> green >> blue;


Comme j'ai déjà dit, ça ne marche pas du tout pour des valeurs binaires.
Tu peux l'oublier. L'opérateur >> interprète toujours un format texte :
il commence par sauter des espaces blancs (c-à-d tout caractère pour
lequel « isspace() » renvoie true dans le locale du fichier). Il saute
donc la fin de ligne que tu n'as pas lu auparavent, mais il saute aussi
toute octet qui a la valeur 9, 10, 11, 12, 13 ou 32 (en supposant locale
"C" et un codage basé sur l'ASCII -- le locale "C" est le défaut, et
tous les ordinateurs courant sauf les gros IBM utilise des codages basés
sur ASCII).

Entre ça et l'absence de l'option binaire lors de l'ouverture, il n'y a
aucune chance que ça marche. Ici, il faudrait encore quelque chose du
genre :

if ( ! skipNL( input ) ) {
// Erreur de formattage...
}
for ( int i = 0 ; i < height ; ++ i ) {
for ( int j = 0 ; j < width ; ++ j ) {
char red ;
input.get( red ) ;
char green ;
input.get( green ) ;
char blue ;
input.get( blue ) ;
if ( ! input ) {
// Erreur de formattage...
}
image->setColor( i, j, red, green, blue ) ;
}
}

À vrai dire, vue qu'on peut estîmer que les erreurs de formattage sont
assez exceptionnelles, je crois que j'écrirais une fonction :

unsigned char
mustGet( std::istream& source )
{
int result = source.get() ;
if ( result == EOF ) {
throw ErreurDeFormattage() ;
}
return result ;
}

Le corps de la boucle deviendra alors :

unsigned char red = mustGet( input ) ;
unsigned char green = mustGet( input ) ;
unsigned char blue = mustGet( input ) ;
image->setColor( i, j, red, green, blue ) ;

Si tu fais ça, il faudrait s'assurer que la reste du code réagit bien
lors d'une exception. Par exemple, si tu as alloué l'image dyanmiquement
(chose que je suppose), il faudrait plutôt utiliser std::auto_ptr pour
le gérer.

Aussi, même avec cette solution, il faut absolument passer par des
variables intermédiaires, et non écrire quelque chose comme :

image->setColor( i,
j,
mustGet( input ),
mustGet( input ),
mustGet( input ) ) ;

image->setColor(i, j, red, green, blue);
}

input.close();
return image;


Si l'interface est figée, et tu ne peux par renvoyer un auto_ptr, il
faudrait faire :

return image.release() ;

ici.

}

En effet, sur la plupart des images, un décalage s'effectue dans la
lecture des données : un saut d'une valeur decale les informations de
couleurs car les valeurs de rouge passent au bleu, celle du vert
passent au rouge et celle du bleu au vert... Un autre saut entraine
d'autre decalages... C'est tres penible.


C'est normal. Tu as dit à la bibliothèque de faire un certain nombre de
translations, et de sauter certains caractères. Il ne faut donc pas
s'étonner à ce qu'elle le fasse.

Une alternative consiste a ecrire :

ifstream input (fileName.c_str (), ios::binary);
if (!input)
{
cerr << "Error : bad file format" << endl;
exit (EXIT_FAILURE);
}

char headerChar;
char headerInt;

input.read(&headerChar, 1);
input.read(&headerInt, 1);

if (headerChar != 'P' || (headerInt != '6' && headerInt != '3'))
{
cerr << "Error : bad file format, need 'P6' file" << endl;
exit (EXIT_FAILURE);
}

int width, height, maxIntensity;
input >> width >> height >> maxIntensity;
//input.getline(0x0, 10);
Image *image = new Image(width, height, maxIntensity);

// Pour lire le n qui fait decaler les couleurs...
char toto;
input.read(&toto, 1);


À ta place, je régarderais les variants de istream::get(). Dans tous les
cas, il fait l'équivalent d'un read(..., 1), et il a souvent une
interface plus commode en cas de la lecture d'un seul caractère.

// Case 'P6' image
if (headerInt == '6')
{
// Colors recording
for (int i = 0 ; i < height ; i++)
for (int j = 0 ; j < width ; j++)
{
unsigned char red, green, blue;
input.read(&red, 1);
input.read(&green, 1);
input.read(&blue, 1);
image->setColor(i, j,
(unsigned char) red,
(unsigned char) green,
(unsigned char) blue);
if (i == 0 && j == 0)
cout << (int) red << " " << (int) green << " " << (int) blue << endl;

}

input.close();
return image;

Mais ce bout de code ne permet de lire que les images correspondant

à la configuration :

P6n

widthn

heightn

maxIntensityn <- ce caractère devient obligatoire et DOIT etre le seul avant le flux binaire

flux binaire sans parasites (pas de n ou t ...)

Quelqu'un a-til une routine de lecture de fichier 'P6' en c++ ?

Sinon, comment lire mon fichier correctement a partir du code fournit?


Au fond, il faut savoir exactement ce qui est acceptable, et ce qui ne
l'est pas. C'est toujours un problème en cas de melange de texte et de
binaire. Quel est la convention qui sert pour séparer les lignes dans
l'en-tête ? Si c'est la convention Windows (CRLF), il faut qu'il soit
toujours la convention Windows, même sur une machine Unix, et vice
versa. Si la fin de la ligne est suivie immédiatement du binaire, il ne
peut pas être parfois CRLF, parfois simplement LF, et parfois simplement
CR. (On pourrait dire que CRLF ou simplement LF soit acceptable.)
Ensuite, il faut que tu écris toi-même du code pour la sauter ; si la
convention accepte CRLF ou LF tout seul, on pourrait écrire quelque
chose du genre :

bool
sautLF( std::istream& source )
{
int ch = source.get() ;
if ( ch == 'r' ) {
ch = source.get() ;
}
return ch == 'n' ;
}

Si tu veux aussi permettre d'autres espaces blanc avant la fin de ligne,
tu pourrais ajouter une boucle au début de cette fonction :

bool
sautLF( std::istream& source )
{
int ch = source.get() ;
while ( std::isspace( ch, locale() ) && ch != 'n' && ch != 'r' ) {
ch = source.get() ;
}
if ( ch == 'r' ) {
ch = source.get() ;
}
return ch == 'n' ;
}

Note, en revanche que tu ne peux pas faire qu'un CR tout seul soit
accepté aussi ; si tu rencontre la séquence 13, 10, est-ce que c'est un
CRLF, ou est-ce que c'est un CR tout seul, suivi d'un rouge dont la
valeur est par hazard 10 ?

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16