LZW

LZW (Lempel-Ziv-Welch) é um algoritmo de compressão de dados, derivado do algoritmo LZ78, baseado na localização e no registro das padronagens de uma estrutura. Foi desenvolvido e patenteado em 1984 por Terry Welch.[1] É geralmente utilizado em imagens em que não se pode perder a definição original. Nas imagens, o algoritmo lê os valores de pixels de uma imagem bitmap e elabora uma tabela de códigos onde se representam as padronagens repetidas dos pixels encontrados.

O codificador LZW reduz, pela compressão, os arquivos de imagens gráficas a 1/3 ou 1/4 de seu tamanho original.

Imagens com padronagem bem definidas — com grandes blocos de cor contínua ou repetidas de cores — podem reduzir para 1/10 o tamanho original do arquivo.

Formatos que utilizam o LZW

  • TIFF, por opção;
  • GIF, por padrão.

Algoritmo

O algoritmo LZW é uma variante do LZ78, que visa eliminar a necessidade de se emitir um caractere literal junto com o endereço de dicionário[2]. Para isso, o dicionário é inicializado com todos os símbolos do alfabeto (ao se usar codificação ASCII são 256 símbolos, de 0 a 255). A entrada é lida e acumulada em uma cadeia de caracteres que chamaremos de I. Sempre que a seqüência contida em I não estiver presente no dicionário emitimos o código correspondente a versão anterior de I (ou seja, I sem o último caractere) e adicionamos I ao dicionário. I volta a ser inicializado com o último caractere lido (o seu último caractere) e o processo continua até que não haja mais caracteres na entrada.

Pseudocódigo

1. No início o dicionário contém todas as raízes possíveis e I é vazio;2. c <= próximo caractere da sequência de entrada;3. A string I+c existe no dicionário?a. se sim,i. I <= I+c;b. se não,i. coloque a palavra código correspondente a I na sequência codificada;ii. adicione a string I+c ao dicionário;iii. I <= c;4. Existem mais caracteres na sequência de entrada ?a. se sim,i. volte ao passo 2;b. se não,ii. coloque a palavra código correspondente a I na sequência codificada;iii. FIM.

Pseudocódigo descompactação String

1. No início o dicionário contém todas as raízes possíveis;2. cW <= primeira palavra código na sequência codificada (sempre é uma raiz);3. Coloque a string(cW) na sequência de saída;4. pW <= cW;5. cW <= próxima palavra código da sequência codificada;6. A string(cW) existe no dicionário ?a. se sim,i. coloque a string(cW) na sequência de saída;ii. P <= string(pW);iii. C <= primeiro caracter da string(cW);iv. adicione a string P+C ao dicionário;b. se não,i. P <= string(pW);ii. C <= primeiro caracter da string(pW);iii. coloque a string P+C na sequência de saída e adicione-a ao dicionário;7. Existem mais palavras código na sequência codificada ?a. se sim,i. volte ao passo 4;b. se não,i. FIM.

Exemplo

No quadro abaixo mostramos os passes da compressão da cadeia A_ASA_DA_CASA usando o método de LZW. A coluna I representa a cadeia lida na entrada desde que a última saída foi emitida. A coluna no dic? indica se a seqüência em I está presente no dicionário. a coluna nova entrada mostra o que será inserido no dicionário (como o dicionário já contém os 256 símbolos do código ASCII é impraticável mostrar todo seu conteúdo, por isso listamos apenas as novas entradas). E por fim a coluna saída mostra o código que será emitido na saída do programa, junto com o que ele representa no dicionário (entre parênteses).

Ino dic?nova entradasaída
AS
A_N256:A_65 (A)
_S
_AN257:_A95 (_)
AS
ASN258:AS65 (A)
SS
SAN259:SA83 (S)
AS
A_S
A_DN260:A_D256 (A_)
DS
DAN261:DA68 (D)
AS
A_S
A_CN262:A_C256 (A_)
CS
CAN263:CA67 (C)
AS
ASS
ASAN264:ASA258 (AS)
AS
---65 (A)

O tamanho final é de 10 códigos,sendo 7 representados por 8 bits cada um e 3 código representados por 9 bits. Nesse caso temos na saída 83 bits, comparados com os 104 originais. Como podemos ver, o método LZW é ainda mais lento para fazer efeito que o método LZ78 devido ao fato de o dicionário já estar inicialmente povoado com todos os símbolos do alfabeto. Entretanto ele é mais simples de ser implementado e gera resultados semelhantes ou até melhores com cadeias de caracteres mais longas.

Problemas

Por ser uma variante do LZ78 o LZW sofre dos mesmos problemas enfrentados por este, que podem ser vistos em LZ78#Problemas.

    • Exemplo de implementação **
#!/usr/bin/python# coding: utf-8import sysclass lzw_encoder:    """        Classe usada para codificar um arquivo usando        o algoritmo LZW. Está é uma implementação de        exemplo que não leva em conta a representação        binária do arquivo de saída nem o estouro do        tamanho do dicionário. Este código foi baseado nas        informações contidas em    """    def __init__(self):        """            Faz a carga inicial do dicionário com os 256            caracteres ASCII possíveis.        """        self.next_code = 0        self.dictionary = dict()        for i in range(0,256):            self.add_to_dictionary(chr(i))    def add_to_dictionary(self, str):        """            Cria um novo código para uma dada "string" e a            insere sob esse código no dicionário        """        self.dictionary[str] = self.next_code        self.next_code = self.next_code + 1    def encode(self, str):        """            Corpo do algoritmo. lê sequencialmente os            caracteres e cria as cadeias a serem inseridas            no dicionário, emitindo os respectivos códigos            no arquivo de saída (representado pela lista "ret"        """        ret = [] # inicializa a lista (arquivo de saída)        buffer = '' # inicializa o acumulador de caracteres lidos        for i in range(0, len(str)):            c = str[i]            # enquanto a cadeia atual existir no dicionário,            # continuamos acresncentando caracteres a ela            # No caso da versao 3, troque a linha de baixo por:            # if len(buffer) == 0 or (buffer + c) in self.dictionary:            if len(buffer) == 0 or self.dictionary.has_key(buffer + c):                buffer = buffer + c            else:                # quando encontramos a maior cadeia presente                # emitimos o código dessa cadeia e criamos uma                # nova cadeia, acrescentando o último                # caractere lido.                code = self.dictionary[buffer]                self.add_to_dictionary(buffer + c)                buffer = c                ret = ret + [code]        if buffer:            ret = ret + [self.dictionary[buffer]]        return retclass lzw_decoder:    """        Classe usada para decodificar um arquivo        codificado com LZW. Segue as mesmas restrições        da classe  lzw_encoder    """    def __init__(self):        """            Faz a carga inicial do dicionário com os 256            caracteres ASCII possíveis.        """        self.next_code = 0        self.dictionary = dict()        for i in range(0,256):            self.add_to_dictionary(chr(i))    def add_to_dictionary(self, str):        """            Cria um novo código para uma dada "string" e a            insere sob esse código no dicionário        """        self.dictionary[self.next_code] = str        self.next_code = self.next_code + 1    def decode(self, symbols):        """            Decodifica o arquivo. Emite sequêncialmente            as cadeias correspondentes aos códigos lidos.            Caso a concatenação dos códigos lidos não            corresponda a uma cadeia existente, acrescenta no            dicionário como uma nova cadeia.        """        last_symbol = symbols[0]        ret = self.dictionary[last_symbol]        for symbol in symbols[1:]:            # No caso da versao 3, troque a linha de baixo por:            # if symbol in self.dictionary:            if self.dictionary.has_key(symbol):                current = self.dictionary[symbol]                previous = self.dictionary[last_symbol]                to_add = current[0]                self.add_to_dictionary(previous + to_add)                ret = ret + current            else:                previous = self.dictionary[last_symbol]                to_add = previous[0]                self.add_to_dictionary(previous + to_add)                ret = ret + previous + to_add            last_symbol = symbol        return retif __name__ == "__main__":    str = ''    str = 'A_ASA_DA_CASA'    encoder = lzw_encoder()    encoded = encoder.encode(str)    print ('encoded:', encoded)    decoder = lzw_decoder()    decoded = decoder.decode(encoded)    print ('decoded:', decoded)

Notas e referências

Bibliografia

Ligaçoes externas

Este artigo sobre programação de computadores é um esboço. Você pode ajudar a Wikipédia expandindo-o.