La famille Netpbm
Le format PPM (Portalbe Pixmap) fait partie de la suite Netpbm, conçue pour le stockage simple d'images. Cette suite comprend trois formats distincts adaptés à différents besoins visuels :
- PBM (Portable Bitmap) : représente des images en noir et blanc pur.
- PGM (Portable Graymap) : conçu pour les images en niveaux de gris.
- PPM (Portable Pixmap) : gère les images en couleurs via le modèle RGB.
Chaque fichier de cette famille est identifié par un numéro magique (magic number) de deux caractères, qui indique à la fois le type d'image et la méthode d'encodage (ASCII ou binaire).
| Numéro magique | Type d'image | Encodage |
|---|---|---|
| P1 | Bitmap | ASCII |
| P2 | Graymap | ASCII |
| P3 | Pixmap | ASCII |
| P4 | Bitmap | Binaire |
| P5 | Graymap | Binaire |
| P6 | Pixmap | Binaire |
Encodage ASCII vs Binaire
L'encodage ASCII produit des fichiers lisibles par l'humain, où les valeurs des pixels sont écrites en texte clair et séparées par des espaces. À l'inverse, l'encodage binaire stocke les données sous forme de flux d'octets continus, sans délimiteurs. Le binaire est beaucoup plus efficace pour le traitement par machine et génère des fichiers de taille réduite.
Structure d'un fichier PPM
Un fichier PPM se divise en deux sections principales : l'en-tête et les données des pixels.
L'en-tête est composé de trois éléments, séparés par des espaces ou des sauts de ligne :
- Le numéro magique (
P3pour ASCII,P6pour binaire). - Les dimensions de l'image : largeur et hauteur exprimées en pixels.
- La valeur maximale de couleur (généralement 255 pour 8 bits par canal).
Il est possible d'insérer des commentaires n'importe où dans l'en-tête. Un commentaire commence par le caractère # et s'étend jusqu'à la fin de la ligne.
Les données pixel :
- En mode ASCII (
P3) : les valeurs RGB sont listées séquentiellement, séparées par des espaces. Un retour à la ligne est souvent utilisé pour délimiter les rangées de pixels. - En mode binaire (
P6) : chaque pixel est encodé sur 24 bits consécutifs (3 octets), correspondant respectivement aux composantes Rouge, Vert et Bleu.
Exemple d'un fichier PPM ASCII de 3x2 pixels :
P3 3 2 255
255 0 0 0 255 0 0 0 255 255 255 0 255 255 255 0 0 0
Implémentation en C
Voici une implémentation robuste pour lire et écrire des fichiers PPM binaires (P6) en utilisant une structure pour encapsuler les données de l'image.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct {
int largeur;
int hauteur;
unsigned char* pixels;
} ImagePPM;
// Fonction utilitaire pour ignorer les commentaires et les espaces
void ignorer_commentaires(FILE* f) {
int c;
while ((c = fgetc(f)) != EOF) {
if (c == '#') {
while ((c = fgetc(f)) != EOF && c != '\n');
} else if (!isspace(c)) {
ungetc(c, f);
return;
}
}
}
ImagePPM* charger_ppm(const char* nom_fichier) {
FILE* f = fopen(nom_fichier, "rb");
if (!f) return NULL;
char type[3];
int val_max;
// Lecture du numéro magique
fscanf(f, "%2s", type);
ignorer_commentaires(f);
ImagePPM* img = (ImagePPM*)malloc(sizeof(ImagePPM));
if (!img) { fclose(f); return NULL; }
// Lecture des dimensions et de la valeur maximale
fscanf(f, "%d", &img->largeur);
ignorer_commentaires(f);
fscanf(f, "%d", &img->hauteur);
ignorer_commentaires(f);
fscanf(f, "%d", &val_max);
// Consomme le caractère de fin de ligne avant les données binaires
fgetc(f);
size_t taille_buffer = img->largeur * img->hauteur * 3;
img->pixels = (unsigned char*)malloc(taille_buffer);
if (!img->pixels) { free(img); fclose(f); return NULL; }
fread(img->pixels, 1, taille_buffer, f);
fclose(f);
return img;
}
void liberer_ppm(ImagePPM* img) {
if (img) {
free(img->pixels);
free(img);
}
}
void sauvegarder_ppm(const char* nom_fichier, const ImagePPM* img) {
FILE* f = fopen(nom_fichier, "wb");
if (!f) return;
fprintf(f, "P6\n%d %d\n255\n", img->largeur, img->hauteur);
fwrite(img->pixels, 1, img->largeur * img->hauteur * 3, f);
fclose(f);
}