-La compression avec Huffman

Transcription

-La compression avec Huffman
-La compression avec Huffman
source :
Cet article est tiré de www.aispirit.tk dont l'auteur est, moi-même
(pour ceux qui n'avait pas compris, c'est un petit clin d'oeil pour que vous veniez visiter mon site)
I- Théorie sur la compression.
Bon, une petite introduction sur la compression, pour s'amuser un peu (et augmenter le nombre de
lignes de ce maigre article :)
1.1
La compression : c'est quoi ?
1.2
Comment ça marche ?
Allez, commençons par une question compliqué !
Alors, le but de la compression, c'est de compresser des données (mais encore ...). Pour ceux qui ne
voit toujours pas (qui ?), le but est diminuer la taille des données, et donc généralement la taille des
fichiers.
Les 2 principaux format de compression sont zip et rar. Je vous conseil à ce propos le logiciel
winrar [1] qui fait tout très bien, et si je me rappelle bien, il est gratuit (ou pas), sinon : quickzip [2]
qui est selon moi moins performant.
Le but est donc de trouvez les chaînes les plus présentes, et de le remplacer par d'autres de taille
plus petite. Mais 2 contraintes surviennent :
– ne pas avoir le même code de compression pour deux chaînes de départ différentes.
– utiliser des codes de compressions préfixes
La première contrainte est assez clair, je trouve, quand à la deuxième, je vais vous l'expliquer un
peu mieux :
imaginions que nous ayons comme code de compression
aab pour la source S et
aabc pour T.
Lorsque l'on lit aabc dans le fichier, on doit remplacer par T ou par S puis garder le c pour la suite ?
Pas facile à dire, et là est le problème.
Un langage préfixe est tel que si (théorie des langage) S appartient au code de compression, on ne
trouvera jamais T tel que S = Tx (x quelconque bien sur) et donc, pour l'autre cas de tout à l'heure,
si on a aabc comme code de compression, aab n'existe pas.
1.3
Les premiers
Une petite salutation aux inventeurs des la compression, et c'est en même temps le moyen pour moi
(comme pour vous, je pense) de les découvrir. En effet, qui est assez motivé pour aller chercher qui
est à l'origine de la compression (eh bien moi :p).
Tout d'abord, je tiens a dire que cette partie a presque faillit (à 2 secondes près) ne pas voir le jour.
Pourquoi ? Tout simplement parce que je ne trouvais aucune information, ou du moins, je cherchais
mal. Bref, voici ce que j'ai trouvé [3]. Accroché-vous, c'est très court !
Le premier vrai algorithme de compression, du moins le premier qui, je cite, a été reconnu est
l'algorithme de Shannon-Fano, crée en 1949. On trouvera ensuite Huffman(1952), Lempel Ziv
(1977), Run Length encoding (ou RLE, date inconnue) et d'autres encore. (à ce propos, pour
information, je pense que je vais coder les différents algos cités ci-dessus, donc ne partez pas trop
longtemps)
II- principe de Huffman
2.1 La théorie
La modélisation de huffman repose sur la création de l'arbre de ... huffman.
Le reste étant un “simple” interfacage avec des fichiers (insistez surtout sur les “ entre “simple” ...)
Sa modélisation se fait de la manière suivante, et respecte bien sûr les contraintes des codes de
compression vus plus haut
a) principe
Donc, comme je viens de le dire (et comme j'adore me répéter...), on va voir tout d'abord comment
fonctionne huffman et comment, en même temps, il respecte les contraintes énumérées ci-dessus.
– le codage est préfixe
– les codages sont optimaux : chaque noeud de l'arbre binaire a deux fils
– le mot de code binaire peut être interprété de la manière suivante :
– 0 : aller vers le fils gauche
– 1 : aller vers le fils droit
Je vous rappelle (cf. 10 lignes plus haut) que huffman repose principalement sur son arbre de
compression et que donc il existe des noeuds, des fils et des feuilles, à savoir :
un fils est soit un noeud, soit une feuille
un noeud possède 2 fils (pour notre cas, mais en théorie, il faut avoir au moins 1 fils)
une feuille ne possède aucun fils
les noeuds (et les feuilles) ont la structure suivante :
typedef struct noeud {
char c;
int val;
struct noeud* filsG;
struct noeud* filsD;
} Noeud;
je dis feuille et noeud car j'utilise la même structure pour les 2. On les distingue par la méthode
bool estFeuille(Noeud* n){
return (n->filsG==NULL && n->filsD==NULL);
}
Il faut savoir que, pour une feuille,
val : représente sa fréquence d'apparition
c : code ascii du caractère
et pour un noeud,
c : est ignoré (généralement mit à NULL)
val : somme des val des 2 fils
Après avoir établi l'arbre de huffman, il ne reste plus qu'à le parcourir pour retrouver le code associé
à chaque caractère, puis ensuite à effectuer la compression par des simplement remplacement dans
le fichier de départ !
Vous verrez que , théoriquement, mon code respecte ce principe (mais ma fainéantise fait que je
risque de dévirer de temps en temps)
b) algorithme de base
avant de lancer directement l'algorithme en C, quelques explications :
on suppose que l'on a crée un file (f) du type suivant :
– possède nbCar éléments.
– Possède la fonction extraitMin, qui se base sur la fréquence d'apparition du caractère.
– Possède ajoutElement qui insère correctement dans la file elle-même
void evolFile(file* f){
int i;
for(i=1;i<nbCar;i++) {
Noeud *z = (Noeud*) malloc(sizeof(Noeud));
z->filsG = (Noeud*) malloc(sizeof(Noeud));
z->filsD = (Noeud*) malloc(sizeof(Noeud));
z->c = equivNull;
*(z->filsG) = extraitMin(f);
*(z->filsD) = extraitMin(f);
z->val=z->filsG->val+z->filsD->val;
ajoutElement(f,*z);
free(z);
}
}
par exemple, pour la liste de départ
L = [(a,45), (b,13),(c,12)(d,16),(e,9),(f,5)]
on obtient,
F = [(f,5),(e,9),(c,12),(b,13), (d,16),(a,45)]
puis l'arbre de huffman
100
(a,45)
55
25
30
(c,12) (b,13)
14
(d,16)
(f,5) (e,9)
et par conséquent, les codes de compression :
[a=0, b=101, c=100,d=111,e=1101,f=1110]
2.2 Exercice
Voici ce qui m'était demandé en tp :
a) sujet :
On part d'une liste de probabilités :
L = [(a,45), (b,13),(c,12)(d,16),(e,9),(f,5)]
(c'est un peu l'exemple ci dessus ...)
But : modéliser l'arbre de huffman
Aide : on dispose des entêtes des fichiers sources pour la modélisation de la file et de huffman (que
je ne vous donnerais pas, car sous licence de mon école, du moins des enseignants, donc ... pas le
droit, mais de toute façon, vous aimez bien inventer à partir de rien :p, ou bien sur à partir des mes
propres entêtes)
b) solution :
Le conseil : lisez le code source du projet, i.e. la totalité, bien au delà du sujet du tp, je vous assure
que vous allez être motivé (sinon, vous allez vite quittez :)
c) aide :
Pour évitez de vous laissez taper comme des fous (j'entends sur votre machine, pas sur la feuille sur
laquelle vous êtes en train de lire cet article), vous trouverez ici [4] la modélisation d'une liste
chaînée avec boucle. Autrement dit (principe de cette modélisation), vous devez réserver un espace
supplémentaire, en plus du nombre d'éléments de la liste.
Pour l'info, c'est cet oubli de ma part qui fait que j'ai passé pas mal de temps à voir pourquoi un
caractère n'était pas mémorisé et que donc, ma modélisation générait une erreur ...
III- code source et explication
Comme je le fait souvent, les explications sont insérées directement dans le code source, bien sur à
l'endroit approprié (non, je ne fais pas n'importe quoi)
Pour éviter de charger l'article, voir [5] pour le code source.
IV- problème rencontré
L'un des problèmes majeurs consistait à stocker l'entête de compression/décompression. Car il faut
en effet savoir comment ensuite revenir au fichier initial !
Les contraintes : structure stable (à moins que vous vouliez tirer au sort), et compacte (là, c'est si
vous aimeriez que le fichier compressé prenne moins de place que le fichier de base)
Cherchez un petit peu et vous verrez que c'est loin d'être facile !
Autant pour la taille que pour la stabilité (notamment lié à la récupération des couples (caractèrecode)
4.1 ma structure
Voici la structure que j'ai adopté :
a) Insertion d'un premier octet spécial
But : obtenir la taille codante du dernier octet de l'archive
cause: au cas où on termine par des 0 pour former un vrai octet
b) Insertion par ordre du code ascii des caractères.
Pour être très clair (surtout pour ceux qui ne comprennent pas le C, mais vu que le projet est
en C, de toute façon, ils s'amuseront déjà avec les sources), voici une modélisation avec une
jolie petite structure :
typedef struct {
octet caractère
octet taille du code
}compressCharCode
c) Insertion du séparateur de 2 octets nul : il est impossible d'avoir une taille de code de 0 (deuxième
octet, car le premier (cf. structure) est le caractère)!
d) Insertion à la volée des codes de compression, tout attaché, sous forme de bit.
C'est à dire : le code 1110 pour \0 et 0111 pour \1 forme l'octet 11100111 soit le code hexa E7 pour
le premier octet, et ainsi de suite.
On termine cette partie, bien entendu, par “boucher les trous”, c'est à dire remplir les derniers bits
du dernier octet du code compression par des 0 (pour former un vrai octet au final et donc faciliter la
suite).
e) On insère ensuite directement le code du fichier (je sais, ça ne fais plus partis de l'entête).
4.2 amélioration
On pourrait très bien la réduire de la manière suivante. Mais beaucoup plus difficile à gérer (et là,
c'est la première version, stable bien sûr, donc ça me suffit pour le moment :p) !
a) Codage de la taille non plus sur un octet mais par exemple 6bits.
Pour la capacité des codes, cela réserve toujours une taille pour un code de 2^6 = 64 caractères.
On gagne ainsi, avec la structure actuelle, 2*256 = 512 bit soit tout de même 64 octets :p, alors que
la structure est beaucoup plus complexe à gérer
b) Nouvelle structure de l'entête si trop de caractère n'apparaissent pas dans le fichier de départ
j'espère que tout ça est maintenant assez clair et que vous serez capable de :
– coder avec huffman (pour ça, c'est pas très dur : prendre mon code qui est parfait)
– analyser et coder d'autres algos de compression.
Dans ce cas, bon courage, surtout si vous voulez créer ensuite une doc d'explication pour les
autres. Et dans ce cas, ne faites pas comme moi : rédigez l'article juste après que votre algo
marche, sinon, il faut reprendre tout le code pour voir ce que vous avez fait (pas malin de ma
part, ça)
Bon coding !
[1] winrar , http://www.rarlab.com/download.htm
[2] quickzip,http://www.quickzip.org
[3] histoire de la compression, ftp://ftp.commentcamarche.net/docs/CNDP-compress.pdf
[4] code source des listes chaînées,
http://www.aispirit.tk ou http://www.oldhopes.tk, section repository/coding
[5] code source expliqué de la compression huffman :
http://www.aispirit.tk ou http://www.oldhopes.tk, section repository/coding