规范的霍夫曼编码比特流的内容是什么?

时间:2017-01-20 12:49:27

标签: algorithm compression gzip huffman-code encoder

让我们考虑以下带有符号代码长度的示例 - 规范代码数据。

A - 2 - 00
B - 2 - 01
D - 2 - 10
C - 3 - 110 
E - 3 - 111

我想知道编码比特流的内容是什么?它是00 01 10 110 111(基本上所有代码)还是二进制等效的2,2,2,3,3作为相应的代码长度?我想在此添加一些资源说只是将代码作为编码比特流传输,而其他资源很少谈论将代码从编码比特流中丢弃并仅传输代码长度数据。

2 个答案:

答案 0 :(得分:2)

编码比特流

代码是:

00 01 10 110 111

请注意,如果我们发送2,2,2,3,3的代码,则无法确定输入是AAACC还是BBBEE(或许多其他等效选项)。

因为霍夫曼码是prefix code,这意味着我们可以明确地解码比特流,尽管不知道空格在哪里。

换句话说,当给出输出000110110111时,我们可以将其唯一解码为ABDCE。

传输代码表

我认为混淆可能是因为你需要拥有两个东西来解码比特流:

  1. 编码比特流
  2. 查找表
  3. 这两件事通常以非常不同的方式编码。

    在许多情况下,查找表是事先修复的,因此不需要传输。

    但是,如果概率可能会发生变化,那么我们需要告诉收件人要使用的代码表。在这种情况下,我们可以只传输每个代码字的长度,这为接收器提供了足够的信息来构造规范的霍夫曼代码。备选方案也是可能的,例如我们可以发送每个代码字长度的数字,后跟值。此替代方法由JPEG使用,并在下面进行了解释。

    实施例

    JPEG图像编解码器使用霍夫曼表。通常使用一些默认表,但可以通过传输自定义霍夫曼代码来优化图像的大小。有关此问题的教程是here

    发送霍夫曼表的方式的另一种描述是here。代码长度发送(以字节为单位),后跟代码值(再次作为字节)。

    读取它的代码(取自链接)是:

    // Next sixteen bytes are the counts for each code length
    u8 counts[16];
    for (i = 0; i < 16; i++) {
        counts[i] = fgetc(fp);
        ctr++;
    }
    
    // Remaining bytes are the data values to be mapped
    // Build the Huffman map of (length, code) -> value
    for (i = 0; i < 16; i++) {
        for (j = 0; j < counts[i]; j++) {
            huffData[table][huffKey(i + 1, code)] = fgetc(fp);
            code++;
            ctr++;
        }
        code <<= 1;
    }
    

答案 1 :(得分:1)

您要问的是如何将代码描述发送给接收方,以便接收方知道如何解码以下代码值。

根据您希望在压缩代码描述方面投入多少精力,有许多方法可以改变复杂程度。 Peter de Rivaz描述了JPEG使用的一种简单方法,即每个长度的代码数量的16个计数,然后是每个符号的字节值。所以对于你的代码(十六进制):

00 03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 41 42 43 44 45

这不是非常紧凑,它不能代表一个可能的代码,即256个8位代码,因为每个长度限制为255个。

您可以做的第一件事就是在拥有完整代码时切断代码长度。很容易计算出剩余的代码模式数量,在这种情况下,只有当没有代码模式时,您可以简单地结束它。跟随符号。然后你有:

00 03 02 41 42 43 44 45

每个计数我们不需要8位,因为它们受到这些计数的限制。例如,您不能有两个以上的一位代码。所以我们可以用更少的比特来编码,例如n个代码的n + 1位。所以两位,三位,等等,直到代码完成。对于您的代码,现在是二进制文件:

00 011 0010

后跟字节41 42 43 44 45,适当地偏移位流。现在,计数列表需要9位而不是24位。因为我们知道只能有256个符号,所以我们可以将每个计数的位数限制为9,允许计数256,解决了之前不存在的问题能够代表扁平代码。然后,如果代码的长度限制为16位(与JPEG相同),则计数所需的最大字节数为14.5,小于原始的16位。通常计数将在14.5字节之前结束。

你可以变得更加复杂,注意到在每个代码长度上,由于使用up模式的代码长度较短,你对该长度代码的可能计数有限制。然后,基于有多少可能的值,每个计数的位数可以是可变的。然后计数描述将是:

00 011 10,然后是8位值41 42 43 44 45

由于我们没有用于长度为1和2的前面的模式,所以仍然需要分别为2和3位。但是,我们现在只有三种可能性留给长度为3:计数0,1或2.计数3将超额预订代码。所以我们可以为最后一个使用两位。它现在是7位而不是9位,这大大减少了使用更长代码长度的代码的位数。

完全不同的方案是deflate格式使用的方案(用于zip,gzip,zlib,png等)。首先发送要遵循的代码长度数,然后是每个符号的代码长度,直到最后一个。符号本身由代码长度位置隐含。这会产生大量的零,以表示不存在的符号。因此,对于你的代码,将有70到达符号69(“E”),然后是65个零,然后是2 2 2 3 3.这看起来非常长,而且确实如此。 deflate然后运行长度和霍夫曼代码的长度列表,以压缩它。长串零被压缩到几位,短长度也只是几位。那么你必须首先发送代码长度代码长度代码(!)的描述,以便你可以解码它。

您可以阅读deflate specification以获取有关该计划的更多信息。 brotli使用类似的方案,仍然更复杂。