2010-02-10 21 views
10

Ho implementated a simple compressor utilizzando il codice puro Huffman sotto Windows.But Non so molto su come decodificare il file compresso in fretta, il mio male algoritmo è:Come decodificare velocemente il codice huffman?

enumerare tutti il ​​codice Huffman nella tabella dei codici poi confrontarlo con i bit nel file compresso.Esso risultato orribile: la decompressione del file 3MB richiederebbe 6 ore.

Potrebbe fornire un algoritmo molto più efficiente? Dovrei usare Hash o qualcosa del genere?

Aggiornamento: ho implementated the decoder con tabella di stato, sulla base di un mio amico advice.I di Lin che questo metodo dovrebbe essere migliore di albero travesal Huffman, 3MB all'interno di 6s.

grazie.

+4

Congratulazioni per il codice ben formattato. Rende il codice WinAPI quasi appetibile. – Manuel

+1

FYI: i decodificatori allo stato dell'arte possono eseguire ~ 1GB/sec (o più) velocità di decompressione su un PC medio attuale: è necessario limitare la lunghezza dei codici huffman a 11/12, utilizzare la tabella di decompressione e utilizzare più flussi di huffman, quindi la dipendenza dei dati è ridotta al minimo. – geza

risposta

16

Un modo per ottimizzare l'approccio binario albero è quello di utilizzare una tabella di ricerca. Si dispone la tabella in modo che sia possibile cercare direttamente un particolare modello di bit codificato, consentendo la larghezza di bit massima possibile di qualsiasi codice.

Poiché la maggior parte dei codici non utilizza la larghezza massima completa, sono inclusi in più posizioni nella tabella: una posizione per ciascuna combinazione di bit inutilizzati. La tabella indica quanti bit eliminare dall'input e l'output decodificato.

Se il codice più lungo è troppo lungo, quindi la tabella non è pratica, un compromesso consiste nell'utilizzare un albero di ricerche con indice inferiore più piccolo. Ad esempio, è possibile utilizzare una tabella di 256 elementi per gestire un byte. Se il codice di input è superiore a 8 bit, la voce della tabella indica che la decodifica è incompleta e ti indirizza a una tabella che gestisce i successivi fino a 8 bit. Le tabelle più grandi scambiano la memoria per la velocità: 256 elementi sono probabilmente troppo piccoli.

Credo che questo approccio generale sia chiamato "tabelle di prefissi" ed è ciò che sta facendo il codice quotato da BobMcGees. Una probabile differenza è che alcuni algoritmi di compressione richiedono che la tabella dei prefissi venga aggiornata durante la decompressione - questo non è necessario per l'Huffman semplice.IIRC, l'ho visto per la prima volta in un libro sui formati di file grafici bitmap che includevano GIF, qualche tempo prima del panico dei brevetti.

Dovrebbe essere facile precalcolare una tabella di ricerca completa, un equivalente di tabella hash o un albero di tabelle di piccole dimensioni da un modello di albero binario. L'albero binario è ancora la rappresentazione chiave del codice: questa tabella di ricerca è solo ottimizzazione.

+0

+1 Questo è il metodo che ho usato nel mio decodificatore JPEG, costruisco la tabella di ricerca direttamente dal segmento DHT nell'immagine non sono necessari problemi con gli alberi binari. – matja

0

Perché non utilizzare l'algoritmo di decompressione nello stesso modulo sorgente? Sembra essere un algoritmo decente.

+3

Non è che il decompressore che l'OP dice impiegherà 6 ore per decomprimere il file 3MB? –

4

Il modo tipico per decomprimere un codice Huffman è l'utilizzo di un albero binario. Inserisci i tuoi codici nell'albero, in modo che ogni bit in un codice rappresenti un ramo a sinistra (0) o a destra (1), con i byte decodificati (o qualunque valore tu abbia) nelle foglie.

La decodifica è quindi solo un caso di lettura di bit dal contenuto codificato, percorrendo l'albero per ogni bit. Quando raggiungi una foglia, emetti quel valore decodificato e continua a leggere finché l'input non è esaurito.

Aggiornamento:this page descrive la tecnica e ha una grafica di fantasia.

+4

Una struttura collegata è molto utile per la costruzione dell'albero, ma penso che una matrice sia la soluzione migliore per eseguire la ricerca. Non deve essere completamente popolato, ma le chiavi a 16 bit in un array di nodi 65536 dovrebbero funzionare bene. NB: non ho mai scritto un decompressore. – Potatoswatter

+0

@Potatoswatter: Quindi, come si passa da un buffer contenente codici Huffman di lunghezza variabile (a livello di bit) a un indice di array? Il punto dell'approccio basato sull'albero è che ti dice quando smettere di leggere i bit. Forse mi manca qualcosa, è da un po 'che non scrivo un decoder Huffman. – unwind

+0

@unwind: è possibile leggere più bit contemporaneamente e utilizzare una tabella di ricerca contenente nodi di ramo o nodi foglia. I nodi Branch danno un puntatore alla tabella per i prossimi bit. I nodi foglia forniscono il valore decodificato e la lunghezza effettiva in modo da sapere quanti bit extra si leggono. Se c'è una lunghezza massima ragionevole (diciamo 16 bit o meno), allora puoi usare una singola tabella di nodi foglia come suggerisce Potatoswatter. –

5

Perché non dare un'occhiata a come funziona lo GZIP source, in particolare il codice di decompressione Huffman in specificamente unpack.c? Sta facendo esattamente quello che sei, tranne che lo sta facendo molto, molto più velocemente.

Da quello che posso dire, sta usando un array di ricerca e operazioni di spostamento/maschera che operano su parole intere per correre più velocemente. Il codice abbastanza denso però.

EDIT: qui è la fonte completa

/* unpack.c -- decompress files in pack format. 
* Copyright (C) 1992-1993 Jean-loup Gailly 
* This is free software; you can redistribute it and/or modify it under the 
* terms of the GNU General Public License, see the file COPYING. 
*/ 

#ifdef RCSID 
static char rcsid[] = "$Id: unpack.c,v 1.4 1993/06/11 19:25:36 jloup Exp $"; 
#endif 

#include "tailor.h" 
#include "gzip.h" 
#include "crypt.h" 

#define MIN(a,b) ((a) <= (b) ? (a) : (b)) 
/* The arguments must not have side effects. */ 

#define MAX_BITLEN 25 
/* Maximum length of Huffman codes. (Minor modifications to the code 
* would be needed to support 32 bits codes, but pack never generates 
* more than 24 bits anyway.) 
*/ 

#define LITERALS 256 
/* Number of literals, excluding the End of Block (EOB) code */ 

#define MAX_PEEK 12 
/* Maximum number of 'peek' bits used to optimize traversal of the 
* Huffman tree. 
*/ 

local ulg orig_len;  /* original uncompressed length */ 
local int max_len;  /* maximum bit length of Huffman codes */ 

local uch literal[LITERALS]; 
/* The literal bytes present in the Huffman tree. The EOB code is not 
* represented. 
*/ 

local int lit_base[MAX_BITLEN+1]; 
/* All literals of a given bit length are contiguous in literal[] and 
* have contiguous codes. literal[code+lit_base[len]] is the literal 
* for a code of len bits. 
*/ 

local int leaves [MAX_BITLEN+1]; /* Number of leaves for each bit length */ 
local int parents[MAX_BITLEN+1]; /* Number of parents for each bit length */ 

local int peek_bits; /* Number of peek bits currently used */ 

/* local uch prefix_len[1 << MAX_PEEK]; */ 
#define prefix_len outbuf 
/* For each bit pattern b of peek_bits bits, prefix_len[b] is the length 
* of the Huffman code starting with a prefix of b (upper bits), or 0 
* if all codes of prefix b have more than peek_bits bits. It is not 
* necessary to have a huge table (large MAX_PEEK) because most of the 
* codes encountered in the input stream are short codes (by construction). 
* So for most codes a single lookup will be necessary. 
*/ 
#if (1<<MAX_PEEK) > OUTBUFSIZ 
    error cannot overlay prefix_len and outbuf 
#endif 

local ulg bitbuf; 
/* Bits are added on the low part of bitbuf and read from the high part. */ 

local int valid;     /* number of valid bits in bitbuf */ 
/* all bits above the last valid bit are always zero */ 

/* Set code to the next 'bits' input bits without skipping them. code 
* must be the name of a simple variable and bits must not have side effects. 
* IN assertions: bits <= 25 (so that we still have room for an extra byte 
* when valid is only 24), and mask = (1<<bits)-1. 
*/ 
#define look_bits(code,bits,mask) \ 
{ \ 
    while (valid < (bits)) bitbuf = (bitbuf<<8) | (ulg)get_byte(), valid += 8; \ 
    code = (bitbuf >> (valid-(bits))) & (mask); \ 
} 

/* Skip the given number of bits (after having peeked at them): */ 
#define skip_bits(bits) (valid -= (bits)) 

#define clear_bitbuf() (valid = 0, bitbuf = 0) 

/* Local functions */ 

local void read_tree OF((void)); 
local void build_tree OF((void)); 

/* =========================================================================== 
* Read the Huffman tree. 
*/ 
local void read_tree() 
{ 
    int len; /* bit length */ 
    int base; /* base offset for a sequence of leaves */ 
    int n; 

    /* Read the original input size, MSB first */ 
    orig_len = 0; 
    for (n = 1; n <= 4; n++) orig_len = (orig_len << 8) | (ulg)get_byte(); 

    max_len = (int)get_byte(); /* maximum bit length of Huffman codes */ 
    if (max_len > MAX_BITLEN) { 
    error("invalid compressed data -- Huffman code > 32 bits"); 
    } 

    /* Get the number of leaves at each bit length */ 
    n = 0; 
    for (len = 1; len <= max_len; len++) { 
    leaves[len] = (int)get_byte(); 
    n += leaves[len]; 
    } 
    if (n > LITERALS) { 
    error("too many leaves in Huffman tree"); 
    } 
    Trace((stderr, "orig_len %ld, max_len %d, leaves %d\n", 
     orig_len, max_len, n)); 
    /* There are at least 2 and at most 256 leaves of length max_len. 
    * (Pack arbitrarily rejects empty files and files consisting of 
    * a single byte even repeated.) To fit the last leaf count in a 
    * byte, it is offset by 2. However, the last literal is the EOB 
    * code, and is not transmitted explicitly in the tree, so we must 
    * adjust here by one only. 
    */ 
    leaves[max_len]++; 

    /* Now read the leaves themselves */ 
    base = 0; 
    for (len = 1; len <= max_len; len++) { 
    /* Remember where the literals of this length start in literal[] : */ 
    lit_base[len] = base; 
    /* And read the literals: */ 
    for (n = leaves[len]; n > 0; n--) { 
     literal[base++] = (uch)get_byte(); 
    } 
    } 
    leaves[max_len]++; /* Now include the EOB code in the Huffman tree */ 
} 

/* =========================================================================== 
* Build the Huffman tree and the prefix table. 
*/ 
local void build_tree() 
{ 
    int nodes = 0; /* number of nodes (parents+leaves) at current bit length */ 
    int len;  /* current bit length */ 
    uch *prefixp; /* pointer in prefix_len */ 

    for (len = max_len; len >= 1; len--) { 
    /* The number of parent nodes at this level is half the total 
    * number of nodes at parent level: 
    */ 
    nodes >>= 1; 
    parents[len] = nodes; 
    /* Update lit_base by the appropriate bias to skip the parent nodes 
    * (which are not represented in the literal array): 
    */ 
    lit_base[len] -= nodes; 
    /* Restore nodes to be parents+leaves: */ 
    nodes += leaves[len]; 
    } 
    /* Construct the prefix table, from shortest leaves to longest ones. 
    * The shortest code is all ones, so we start at the end of the table. 
    */ 
    peek_bits = MIN(max_len, MAX_PEEK); 
    prefixp = &prefix_len[1<<peek_bits]; 
    for (len = 1; len <= peek_bits; len++) { 
    int prefixes = leaves[len] << (peek_bits-len); /* may be 0 */ 
    while (prefixes--) *--prefixp = (uch)len; 
    } 
    /* The length of all other codes is unknown: */ 
    while (prefixp > prefix_len) *--prefixp = 0; 
} 

/* =========================================================================== 
* Unpack in to out. This routine does not support the old pack format 
* with magic header \037\037. 
* 
* IN assertions: the buffer inbuf contains already the beginning of 
* the compressed data, from offsets inptr to insize-1 included. 
* The magic header has already been checked. The output buffer is cleared. 
*/ 
int unpack(in, out) 
    int in, out;   /* input and output file descriptors */ 
{ 
    int len;    /* Bit length of current code */ 
    unsigned eob;   /* End Of Block code */ 
    register unsigned peek; /* lookahead bits */ 
    unsigned peek_mask;  /* Mask for peek_bits bits */ 

    ifd = in; 
    ofd = out; 

    read_tree();  /* Read the Huffman tree */ 
    build_tree(); /* Build the prefix table */ 
    clear_bitbuf(); /* Initialize bit input */ 
    peek_mask = (1<<peek_bits)-1; 

    /* The eob code is the largest code among all leaves of maximal length: */ 
    eob = leaves[max_len]-1; 
    Trace((stderr, "eob %d %x\n", max_len, eob)); 

    /* Decode the input data: */ 
    for (;;) { 
    /* Since eob is the longest code and not shorter than max_len, 
     * we can peek at max_len bits without having the risk of reading 
     * beyond the end of file. 
    */ 
    look_bits(peek, peek_bits, peek_mask); 
    len = prefix_len[peek]; 
    if (len > 0) { 
     peek >>= peek_bits - len; /* discard the extra bits */ 
    } else { 
     /* Code of more than peek_bits bits, we must traverse the tree */ 
     ulg mask = peek_mask; 
     len = peek_bits; 
     do { 
       len++, mask = (mask<<1)+1; 
     look_bits(peek, len, mask); 
     } while (peek < (unsigned)parents[len]); 
     /* loop as long as peek is a parent node */ 
    } 
    /* At this point, peek is the next complete code, of len bits */ 
    if (peek == eob && len == max_len) break; /* end of file? */ 
    put_ubyte(literal[peek+lit_base[len]]); 
    Tracev((stderr,"%02d %04x %c\n", len, peek, 
     literal[peek+lit_base[len]])); 
    skip_bits(len); 
    } /* for (;;) */ 

    flush_window(); 
    Trace((stderr, "bytes_out %ld\n", bytes_out)); 
    if (orig_len != (ulg)bytes_out) { 
    error("invalid compressed data--length error"); 
    } 
    return OK; 
} 
1

È possibile eseguire una sorta di ricerca lotto sul solito Huffmann albero di ricerca:

  1. La scelta di una profondità di bit (lo chiamano profondità n); questo è un compromesso tra velocità, memoria e investimento nel tempo per costruire tabelle;
  2. Creare una tabella di ricerca per tutti 2^n stringhe di bit di lunghezza n. Ogni voce può codificare diversi token completi; ci saranno anche alcuni bit rimasti che sono solo un prefisso dei codici di Huffman: per ognuno di questi, crea un link a un'ulteriore tabella di ricerca per quel codice;
  3. Creare le ulteriori tabelle di ricerca. Il numero totale di tabelle è al massimo un numero in meno rispetto al numero di voci codificate nell'albero di Huffmann.

La scelta di una profondità che è un multiplo di quattro, ad esempio la profondità 8, è adatta per le operazioni di spostamento dei bit.

Postscript Questo differisce dall'idea di commento di potatoswatter sulla risposta di svolgimento e dalla risposta di Steve314 nell'uso più tabelle: questo significa che tutte le lookup n bit è mettere da usare, quindi dovrebbe essere più veloce, ma marche la costruzione della tabella e la ricerca sono notevolmente più complicate e consumano molto più spazio per una determinata profondità.

Problemi correlati