Skip to content

Sachaaaaaa/JpegDecoder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Décodeur JPEG

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.

Structure du code

├── 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 !

Compilation et exécution

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 tests

Pour exécuter le programme, il faut utiliser la commande suivante :

./jpeg2ppm <chemin_fichier>.jpg  # ou .jpeg

Structure modulaire du décodeur JPEG

Le 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.

Modules et responsabilités

1. Module jpeg_header

  • 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_header contenant :
    • 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).

2. Module huffman

  • 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.

3. Module IQZZ (Inverse Quantification & Zig-Zag)

  • 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.

4. Module IDCTR (Inverse Discrete Cosine Transform Rapide)

  • 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.

5. Module load_blocs

  • Fichiers : src/main.c (fonction load_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.

6. Module reconstruct_matrix

  • 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.

7. Module toRGB

  • 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).

9. Gestion mémoire et nettoyage

  • Fonctions : free_all() dans main.c
  • Rôle : libération systématique de toutes les allocations mémoire (blocs, matrices, structures Huffman, header, buffers).

10. Module main

  • Fichiers : src/main.c
  • Entrée : argc nombre d'arguments de la commande, argv les 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.

Structures de données

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.

Schéma structure de données

Étapes principales

Le pipeline suit la séquence suivante :

  1. Lecture de l'en-tête du fichier JPEG
  2. Reconstruction des arbres de Huffman
  3. Récupération des coefficients DC et AC de chaque bloc
  4. Déquantification des coefficients DCT
  5. Application de l'iDCT(R) et du zig-zag sur chaque bloc 8x8
  6. Reconstruction des composantes Y, Cb, Cr
  7. Up-sampling des composantes Cb et Cr
  8. Conversion YCbCr en RGB
  9. 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.

Références

[BONUS] Avancement

Étape Description Statut Tests Complexité
1 Lecture de l'en-tête JPEG :
  • dimensions,
  • tables de quantification (précision, indice et valeurs),
  • tables de Huffman (type AC ou DC, indice, nb de symboles par longueur de code, symboles),
  • association composante / table quantification / Huffman AC / Huffman DC,
  • facteur d’échantillonnage pour chaque composante
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) ★★

[BONUS] Script shell pour facteurs d'échantillonnage

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 .ppm ou pgm,
  • 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.sh

Voici 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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •