Ce repository implémente un décodeur JPEG en mode baseline sequential, conforme à la norme ITU-T T.81 (JPEG JFIF). Il permet de décoder les images .jpg/.jpeg, en utilisant uniquement les bibliothèques standard du langage C (stdio, stdint, stdlib, etc.).
Le projet a une vocation principalement pédagogique. Il met l'accent sur la prise de décisions techniques raisonnées selon les contraintes rencontrées, comme :
- Le choix des structures de données,
- Les startégies d'allocation mémoire (par exemple : tableaux statiques vs tableaux dynamiques, mémoire contiguë pour éviter les cache miss, gestion explicite de la libération mémoire),
- L'équilibre entre la vitesse d'exécution et empreinte mémoire,
- La lisibilité et la maintenabilité du code (faut-il introduire des structures intérmediaires pour rendre le code plus clair et éviter des accès à base de modulo et de divisions, au prix d'un léger surcoût en performance ?)
L'ensemble du pipeline de décodage a été conçu avec rigueur, réfléchi et reréfléchi, au dépit du mode progressif, qui a été délaissé pour se concentrer sur un code robuste, documenté, commenté, testé, aéré, lisible, sans fuite mémoire et aussi complet que possible pour le cadre du mode baseline, en prenant en compte une grosse quantité de source d'erreur.
Vous trouverez tout de même une branche progressive, qui implémente un début de décodage d'image JPEG progressive.
├── bin/ # Contient les exécutables compilés
│
├── images/ # Fichiers JPEG utilisés pour les tests manuels
│ └── special_images/ # Cas particuliers (échantillonnages 1x4, etc.)
│
├── include/ # Fichiers d'en-tête (.h)
│ ├── huffman.h
│ ├── jpeg_header.h
│ ├── IDCTR.h
│ ├── IQZZ.h
│ ├── memory_utils.h
│ ├── toRGB.h
│ ├── up_sampling.h
│ ├── test_tools.h
│ └── type.h
│
├── obj/ # Fichiers objets (.o) générés à la compilation
│
├── src/ # Code source principal (.c)
│ ├── huffman.c
│ ├── jpeg_header.c
│ ├── IDCTR.c
│ ├── IQZZ.c
│ ├── memory_utils.c
│ ├── toRGB.c
│ ├── type.c
│ ├── up_sampling.c
│ ├── main.c # Point d'entrée principal
│ └── tests/ # Tests unitaires
│ ├── test_huffman.c
│ ├── test_IDCTR.c
│ ├── test_IQZZ.c
│ ├── test_toRGB.c
│ ├── test_up_sampling.c
│ └── test_tools.c
│
├── test_files/ # Fichiers annexes pour les tests (fausse en-tête pour tester Huffman, par exemple)
│
├── Makefile # Script de compilation
└── README.md # Salut ! c'est moi !Pour compiler le programme, ces simples commandes suffisent :
make [all] # Compilation complète ("all" pas nécessaire)
make test # Compilation des tests unitaires
make clean # Suppression des fichiers objets et exécutables
make run_tests # Exécution de tous les testsPour exécuter le programme, il faut utiliser la commande suivante :
./jpeg2ppm <chemin_fichier>.jpg # ou .jpegLe décodeur est organisé en modules fonctionnels correspondant aux étapes principales du pipeline de décodage JPEG baseline sequential (décrites dans la section en dessous). Chaque module reçoit en entrée des données précises et produit des résultats exploités par l’étape suivante.
- Fichiers :
src/jpeg_header.c,include/jpeg_header.h - Fonction principale :
jpeg_header* get_header(FILE *fptr) - Entrée : pointeur de fichier JPEG (
FILE *) - Sortie : structure
jpeg_headercontenant :- dimensions de l’image,
- composantes (facteurs d’échantillonnage, indices de table de quantification et de tables de Huffman AC et DC),
- tables de quantification (précision, indice et valeurs),
- tables de Huffman AC et DC (type, indice, nombre de symboles par longueur de code, symboles).
- Rôle : analyse et extraction complète des données à partir de l’en-tête JPEG (sections SOI, APP0, COM, DQT, DHT, SOF0, SOS).
- Fichiers :
src/huffman.c,include/huffman.h - Fonctions principales : génération des arbres de décodage Huffman et extraction des coefficients AC/DC.
- Entrée : tables Huffman issues du header, flux de données du fichier (fin de section SOS),
- Sortie : arbres de Huffman, coefficients DC et AC décodés des blocs.
- Rôle : reconstruction des arbres Huffman et décodage des flux de bits pour extraire les coefficients quantifiés.
- Fichiers :
src/IQZZ.c,include/IQZZ.h - Fonctions principales :
quantif_inv(),zig_zag_inv() - Entrée : coefficients quantifiés en zig-zag (tableau 1D)
- Sortie : blocs 8x8 de coefficients DCT déquantifiés ordonnés en matrice 2D
- Rôle : inverse la quantification puis applique l’ordre inverse du zig-zag pour reconstituer la matrice 8x8 de coefficients DCT.
- Fichiers :
src/IDCTR.c,include/IDCTR.h - Fonction principale :
apply_idctr() - Entrée : matrice 8x8 de coefficients DCT déquantifiés
- Sortie : matrice 8x8 de pixels dans l’espace Y, Cb ou Cr
- Rôle : applique la transformation inverse rapide de cosinus discrète pour passer des coefficients fréquentiels aux valeurs spatiales.
- Fichiers :
src/main.c(fonctionload_blocs) - Entrée : header JPEG, tables Huffman, état du buffer, nombre de MCUs
- Sortie : liste chaînée de blocs 8x8 après décodage, IQZZ et IDCT
- Rôle : lit et décode séquentiellement tous les blocs MCU de l’image, organise les blocs dans une liste chaînée pour traitement ultérieur.
- Fichiers :
src/up_sampling.c,src/up_sampling.h - Entrée : liste chaînée de blocs, paramètres d’échantillonnage, dimensions MCU
- Sortie : matrice 3D de MCUs en YCbCr avec gestion du sous-échantillonnage
- Rôle : organise les blocs décodés en matrice selon la structure MCU, applique l’up-sampling si nécessaire.
- Fichiers :
src/toRGB.c,include/toRGB.h - Fonction principale :
toRGB()(La fonction est bien documentée, promis) - Entrée : matrice MCU YCbCr, paramètres composants, dimensions image
- Rôle : convertit les composantes YCbCr en RGB et écrit un nouveau fichier
<chemin_fichier>.ppmà partir des pixels convertis, le tout ligne de bloc par ligne de bloc (c'est-à-dire toute une ligne de l'image).
- Fonctions :
free_all()dansmain.c - Rôle : libération systématique de toutes les allocations mémoire (blocs, matrices, structures Huffman, header, buffers).
- Fichiers :
src/main.c - Entrée :
argcnombre d'arguments de la commande,argvles arguments eux même. - Sortie : L'image JPEG décodée en
.ppm - Rôle : décode une image JPEG en mode baseline sequential et produit un fichier
.ppm.
Voici un schéma illustrant les structures de données du projet. Vous aurez beaucoup plus de détails dans le code, chaque fonction et chaque structure ayant été fortement documentée.
Le pipeline suit la séquence suivante :
- Lecture de l'en-tête du fichier JPEG
- Reconstruction des arbres de Huffman
- Récupération des coefficients DC et AC de chaque bloc
- Déquantification des coefficients DCT
- Application de l'iDCT(R) et du zig-zag sur chaque bloc 8x8
- Reconstruction des composantes Y, Cb, Cr
- Up-sampling des composantes Cb et Cr
- Conversion YCbCr en RGB
- Assemblage de l’image complète
Chaque module est conçu pour être indépendant, avec des entrées/sorties clairement définies, ce qui facilite la maintenance, le test unitaire et l’évolution du projet.
| Étape | Description | Statut | Tests | Complexité |
|---|---|---|---|---|
| 1 | Lecture de l'en-tête JPEG :
|
Terminé |
Testé (Tests manuels : le programme termine en cas d'erreur) |
★★★★ |
| 2 | Reconstruction des arbres de Huffman à partir des segments DHT : génération des arbres de décodage selon les longueurs et symboles | Terminé |
Testé |
★★★ |
| 3 | Décodage des blocs MCU via Huffman : lecture du bitstream, décodage des coefficients DC différentiels et AC RLE pour chaque composante | Terminé |
Testé |
★★★ |
| 4 | Déquantification des coefficients DCT et zig-zag : multiplication des vecteurs bloc par leur table de quantification respective, puis application du zig-zag pour obtenir des matrices bloc | Terminé |
Testé |
★ |
| 5 | Application de l’iDCT(R) (ici, l'iDCT rapide) sur chaque bloc 8x8 pour retrouver les valeurs spatiales (pixels Y, Cb ou Cr) | Terminé |
Testé |
★★★ |
| 6 | Reconstruction des composantes Y, Cb, Cr sur toute l’image à partir des blocs, en tenant compte des facteurs d’échantillonnage | Terminé |
Non testé |
★★★ |
| 7 | Up-sampling des composantes Cb et Cr : redimensionnement pour qu'elles aient la même résolution que Y | Terminé |
Testé |
★★★★★ |
| 8 | Conversion YCbCr en RGB : application des formules de conversion avec saturation entre 0 et 255 | Terminé |
Testé |
★★ |
| 9 | Assemblage de l’image finale en RGB à partir des blocs convertis, gestion des bordures éventuelles | En cours |
Non testé (résultat final, donc facilement vérifiable) |
★★ |
Pour tester facilement tous les facteurs d'échantillonnage différent possible, nous avons mis en place un script shell qui :
- Crée un fichier JPEG pour toutes les combinaisons de facteurs d'échantillonnage possibles (dans
images/sampling), - Convertit chaque fichier JPEG en
.ppmoupgm, - Ouvre chaque fichier convertit avec EOG.
Il suffit de la placer à la racine du projet, puis de donner les droits d'exécution avec :
chmod u+x script.shVoici le script en question :
#!/bin/bash
mkdir -p "images/sampling"
INPUT_FILE="images/sample.jpg"
INPUT_DIR="images/sampling"
OUTPUT_DIR="images/sampling"
#############################################################
# Création de tous les facteurs d'échantillonnage possibles #
#############################################################
counter=0
for hY in {1..4}; do
for vY in {1..4}; do
for hCb in {1..4}; do
for vCb in {1..4}; do
for hCr in {1..4}; do
for vCr in {1..4}; do
# Contrainte 1 : somme des produits <=10
sum=$((hY*vY + hCb*vCb + hCr*vCr))
if [ "$sum" -gt 10 ]; then
continue
fi
# Contrainte 2 : sous-échantillonnage cohérent
if (( hY % hCb != 0 || vY % vCb != 0 || hY % hCr != 0 || vY % vCr != 0 )); then
continue
fi
filename="${counter}_${hY}x${vY}_${hCb}x${vCb}_${hCr}x${vCr}.jpg"
sample_option="${hY}x${vY},${hCb}x${vCb},${hCr}x${vCr}"
echo "=> Génèration de $filename (-sample $sample_option)"
djpeg "$INPUT_FILE" | cjpeg -sample "$sample_option" -quality 90 > "$OUTPUT_DIR/$filename"
counter=$((counter + 1))
done
done
done
done
done
done
######################
# Conversion en .ppm #
######################
for img in "$INPUT_DIR"/*.jpg; do
echo "=> Conversion en .ppm de $img"
./jpeg2ppm "$img"
done
######################
# Affichage dans EOG #
######################
for img in "$INPUT_DIR"/*.ppm; do
[ -f "$img" ] || continue
echo "=> Ouverture de $img"
eog "$img" &
done