Compression d'images BMP en niveaux de gris avec Qt et programmation dynamique

La compression d'images BMP en niveaux de gris avec Qt, combinée à l'algorithme de programmation dynamique, nécessite la maîtrise de trois domaines : le traitement d'images, l'analyse de formats de fichiers et l'optimisation par programmation dynamique.

Analyse du format BMP et traitement en niveaux de gris

1. Structure du fichier BMP

Un fichier BMP se compose d'un en-tête de fichier (14 octets), d'un en-tête d'information (40 octets), d'une table de couleurs (optionnelle) et des données de pixels. Les images en niveaux de gris utilisent généralement le format BMP 8 bits, contenant 256 niveaux de gris, où chaque entrée de la table de couleurs correspond à une valeur de gris (par exemple, l'index 0 pour le noir, 255 pour le blanc).

2. Implémentation du traitement en niveaux de gris

  • Conversion couleur en gris : Utiliser la formule Y = 0.299R + 0.587G + 0.144B pour calculer la valeur de gris de chaque pixel.
  • Traitement BMP 8 bits : Après lecture de la table de cuoleurs, mapper les valeurs RGB de chaque pixel à un indice de gris, et mettre à jour les entrées de la table de couleurs avec les valeurs de gris correspondantes.
  • Exemple d'implémentation Qt :
QImage image("entree.bmp");
for (int y = 0; y < image.height(); y++) {
    for (int x = 0; x < image.width(); x++) {
        QColor couleur = image.pixelColor(x, y);
        int gris = static_cast<int>(0.299 * couleur.red() + 0.587 * couleur.green() + 0.144 * couleur.blue());
        image.setPixelColor(x, y, QColor(gris, gris, gris));
    }
}


Implémentation de l'algorithme de programmation dynamique pour la compression d'images

1. Modélisation du problème

  • Objectif : Diviser la séquence de gris en segments, chacun étant stocké avec le minimum de bits (le nombre de bits étant déterminé par la valeur maximale de gris dans le segment), et ajouter des informations d'en-tête fixes (3 bits pour le nombre de bits, 8 bits pour la longueur du segment), afin de minimiser l'espace de stockage total.
  • Contrainte : Chaque segment peut contenir au maximum 256 pixels (la valeur maximale représentable sur 3 bits).

2. Définition des états de la programmation dynamique

  • s[i] : Espace de stockage optimal pour les i premiers pixels.
  • l[i] : Nombre de pixels dans le ième segment.
  • b[i] : Nombre de bits pour la valeur maximale de gris dans le ième segment.

3. Équation de transition d'état

s[i] = min_{1≤k≤min(i,256)} { s[i-k] + k * b_max + 11 }


  • b_max est le nombre de bits pour la valeur maximale de gris du segment actuel.
  • 11 représente le nombre fixe de bits pour les informations d'en-tête (3 bits de longueur + 8 bits de nombre de bits).

4. Reconstruction de la solution optimale par retour arrière

En enregistrant les points de分割 optimaux pour chaque segment, on peut restaurer de manière rétrograde le plan de segmentation et générer le flux de données compressé.

Étapes d'implémentation dans Qt

1. Lecture du fichier BMP

Utilsier QFile pour lire l'en-tête du fichier, l'en-tête d'information, la table de couleurs et les données de pixels. Pour un BMP 8 bits, il faut analyser la table de couleurs et mapper les valeurs de gris.

2. Extraction de la séquence de gris

Convertir les données de pixels en séquence de valeurs de gris p[], stockée dans un tableau d'entiers.

3. Compression par programmation dynamique

  • Initialisation des tableaux : s[0] = 0, l[] et b[] initialisés à 0.
  • Parcours des pixels : Pour chaque pixel i, calculer toutes les longueurs de segment possibles k (1~256), et mettre à jour s[i], l[i] et b[i].
  • Optimisation des calculs : Réduire les calculs redondants en pré-calculant le nombre de bits pour les valeurs maximales de gris (fonction tailleBits()).

4. Génération du fichier compressé

  • Données compressées : Stocker les informations de segmentation (longueur de segment, nombre de bits maximum) et les données de pixels selon le format compressé.
  • Mise à jour de l'en-tête BMP : Modifier la taille du fichier (bfSize) et le décalage des données de bitmap (biSizeImage) dans l'en-tête du fichier.

Exemple de code (parties essentielles)

// Fonction de compression par programmation dynamique
void Compresser(int n, int valeurs[], int couts[], int longueurs[], int nbBits[]) {
    int Lmax = 256, entete = 11;
    couts[0] = 0;
    for (int i = 1; i <= n; i++) {
        nbBits[i] = tailleBits(valeurs[i]);  // Calculer le nombre de bits pour le pixel actuel
        int bmax = nbBits[i];
        couts[i] = couts[i-1] + bmax + entete;
        longueurs[i] = 1;
        for (int j = 2; j <= i && j <= Lmax; j++) {
            if (bmax < tailleBits(valeurs[i-j+1])) bmax = tailleBits(valeurs[i-j+1]);
            if (couts[i] > couts[i-j] + j*bmax + entete) {
                couts[i] = couts[i-j] + j*bmax + entete;
                longueurs[i] = j;
                nbBits[i] = bmax;
            }
        }
    }
}

// Retour arrière pour générer les informations de segmentation
void RetourArriere(int n, int &m, int longueurs[], int nbBits[]) {
    if (n == 0) return;
    RetourArriere(n - longueurs[n], m, longueurs, nbBits);
    longueurs[m] = longueurs[n];
    nbBits[m] = nbBits[n];
    m++;
}


Optimisations des performances et considérations importantes

1. Gestion de la mémoire

Pour les fichiers BMP volumineux, il faut lire les données de pixels par blocs pour éviter le dépassement de mémoire.

2. Contrôle de l'erreur de quantification

La programmation dynamique peut entraîner une distorsion des valeurs de gris en raison de la segmentation ; il faut donc définir un seuil d'erreur.

3. Mise à jour de l'en-tête du fichier

Après compression, il faut recalculer les champs bfSize et biSizeImage dans l'en-tête du fichier BMP.

4. Accélération des calculs

Pré-calculer un tableau de nombres de bits pour les valeurs de gris (par exemple, le tableau tailleBits[]) pour réduire les calculs redondants.

Étiquettes: Qt BMP compression d'images programmation dynamique traitement d'images

Publié le 29 mai à 08h36