3. Qualité et performance de la compression JPEG

<< Utilisation de la DCT pour la compression JPEG | Liste des exercices | >>

Le but de cet exercice est d'évaluer (grossièrement) la qualité et les performances de la compression par quantification du résultat de la DCT, qui est l'étape majeure de l'algorithme JPEG.

Évaluation de la distorsion

Pour évaluer la qualité du résultat, on mesure la différence (en valeur absolue ou quadratique) entre l'image obtenue et l'image originale. Les mesures les plus classiques sont ainsi l'erreur absolue moyenne ($MAE$), l'erreur quadratique moyenne ($MSE$) et le rapport signal sur bruit pic-à-àpic ($PNSR$). En ne considérant que le plan de luminance $Y$ sur 8 bits comme c'est le cas ici, ceux-ci s'écrivent :

$MAE = \frac{1}{MN} \sum_{n=0}^{N-1} \sum_{m=0}^{M-1} \left| Y_{m,n}-\hat{Y}_{m,n} \right| $

$MSE = \frac{1}{MN} \sum_{n=0}^{N-1} \sum_{m=0}^{M-1} \left( Y_{m,n}-\hat{Y}_{m,n} \right) ^2$
$PSNR=10 \cdot log_{10} \left( \frac{255^2}{MSE} \right)$

  1. Sur l'image « Lena », appliquer la compression/décompression, puis évaluer la distorsion en utilisant simplement les fonctionnalités d'ImageJ disponibles dans les menus (par exemple, Process/Image Calculator/Difference, qui retourne la différence absolue pixel à pixel, Process/Math/* qui permet diverses opérations et Analyse/Measure qui donne des statistiques sur l'image entière).

Influence de la quantification

Le « facteur de qualité JPEG », que l'on retrouve dans de nombreux logiciels de retouche d'images, n'a malheureusement pas de définition standard (cf. par exemple http://www.impulseadventure.com/photo/jpeg-quality.html). Mais le principe en reste principalement basé sur la quantification plus ou moins « sévère » des coefficients DCT. Pour cette étude, nous retiendrons ici simplement la définition d'une nouvelle matrice de quantification$ :

$Q^* (u,v) = \alpha \cdot Q(u,v)$ , avec $\alpha=\left\{ \begin{array}{ll} \frac{50}{q} & \mathrm{ si } 1 \leq q \leq 50\\ 2-\frac{2 q}{100} & \mathrm{ si } 51 \leq q \leq 99\\ \end{array}$

$q \in [1,99]$ est le facteur de qualité JPEG. Fixé à 1, il fournit une compression maximale (donc une qualité minimale). Sa limite de 100, correspondant à l'implémentation réalisée jusqu'à présent, fournit une compression minimale (donc une qualité maximale).

Remarque : en pratique, la formule de $q$ ci-dessus est réservée à des valeurs inférieures à 97.

  1. Compléter le plugin pour pouvoir appliquer la compression avec un facteur de qualité variable.
  2. Compresser l'image « Lena » avec différents facteurs de qualité entre 5 et 75. Évaluer la distorsion de l'image décompressée, visuellement puis avec les mesures évoquées à la question 1.

Ratio de compression

Afin d'évaluer la performance de la compression en s'approchant au mieux de celle de JPEG mais sans implémenter le codage entropique, on propose le compromis suivant :

  • tous les coefficients sont traités de la même façon (pas de distinction entre DC et AC) ;
  • le codage RLE est remplacé par la simple détection de la fin du bloc (ang. « end of block », EOB en abrégé), au-delà de laquelle tous les coefficients sont nuls (dans l'ordre de lecture en zigzag) ;
  • le codage de Huffman des coefficients situés avant EOB est remplacé par une compression PNG de ces seuls coefficients restants.

On fournit ici la méthode qui retourne les coefficients d'un bloc ordonnés selon le parcours zigzag :

/**
* Génère la séquence des valeurs résultant de la lecture zigzag (parcours des coefficients DCT pour JPEG)
* @param fpBlock bloc (carré) de coefficients DCT (donnés dans un ImageProcessor)
* @return out Séquence des coefficients DCT suite à lecture zigzag
*/
private static float[] zigzagSeq(ImageProcessor fpBlock) {

    int size = fpBlock.getWidth();	// taille du bloc
    int moitie = size*(size+1)/2;	// nombre de coefs sur-diagonaux
    float[] out = new float[size*size];

    int nCoefs = 0;	// nb de coefs déjà parcourus
    // Parcours de la d-ième diagonale (numérotée à partir de 0)
    for (int d = 0; d < 2*size-1; d++) {
        if (d%2 == 0) {	//Diagonale paire
            for (int x = (nCoefs<moitie?0:d-size+1); x<=(nCoefs<moitie?d:size-1); x++) {
                out[nCoefs++]=fpBlock.getPixelValue(x,d-x);
            }
        } else { // Diagonale impaire
            for (int x = (nCoefs<moitie?d:size-1); x>=(nCoefs<moitie?0:d-size+1); x--) {
                out[nCoefs++]=fpBlock.getPixelValue(x,d-x);
            }
        }
    }
    return out;
}
  1. Implémenter la méthode
private static List<Float> coefsBeforeEOB(ImageProcessor f)

qui retourne les coefficients, dans l'ordre de lecture zizag, situés avant EOB dans le bloc désigné par la ROI de l'image f.

  1. Dans la méthode run du plugin, après la compression de chaque bloc, appeler la méthode précédente pour compléter une liste de tous les coefficients situés avant EOB, liste déclarée par exemple grâce à :
List<Float> allCoefsBeforeEOB = new ArrayList<Float>();
  1. Toujours dans la méthode run, créer une image (FloatProcessor) avec les valeurs de allCoefsBeforeEOB sur une ligne unique de pixels. Après avoir ajouté 128 à tous ses pixels, enregistrer cette image au format PNG grâce à un objet FileSaver.
  2. En examinant la taille du fichier ainsi généré, calculer le ratio de compression atteint par cet algorithme et ce, pour les mêmes valeurs de facteur de qualité que celles testées à la question 3.