寻找更好的压缩技术

时间:2012-11-12 18:23:46

标签: algorithm compression

我正在压缩由数据包组成的二进制流

数据包由256个32位整数(样本)组成。问题是大多数整数只改变了前一个整数中的几个位(通常0到4位最多来自流中的前一个样本)。

以下是一个例子:

3322 2222 2222 1111 1111 1110 0000 0000    BIT POSITIONS
1098 7654 3210 9817 6543 2109 8765 4321
--------------------------------------------------------
1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  
               *                   * 
1100 1001 1110 1010 0001 0101 0110 0101    Sample 2     changes: bit 19, 4

1100 1001 1110 1010 0001 0101 0110 0101    Sample 3     changes: none
     *            *            *   
1100 0001 1110 1011 0001 0101 0010 0101    Sample 4     changes: bit 27, 17, 7
...

我目前的损失压缩方案基于半字节。基本上我正在使用一个控制字节,我正在编码 - 使用单个位 - 从前一个样本改变了半字节;如果有变化,我会在压缩流中包含修改的半字节,否则它们将在解压缩时从前一个样本重建。

以下是我提供的示例流将如何压缩:

Control Byte: 11111111     // all nibbles change, since this is first sample
Data:         1100 1001 1110 0010 0001 0101 0110 1101 // data for all nibbles
Control Byte: 00010001     // only nibbles 3 and 7 have changes
Data:         1010 0101    // data for nibbles 3 and 7
Control Byte: 00000000     // no nibbles are changing
Data:                      // no data is required
Control Byte: 01010010     // nibbles 1, 3 and 6 have changes
Data:         0001 1011 0010   // nibbles 1, 3 and 6
...

使用这种方案,我们有256字节(控制字节)的固定开销,平均可变压缩数据长度为260字节(从样本到样本的半字节变化)。考虑到未压缩的数据包长度为1024字节,这实际上给了我们50%的平均压缩率。

这不错,但我的直觉是,更好的方法是可行的。是否有人意识到一种更好的压缩策略,它利用了从样本到样本的极少数位变化的事实?只要解压缩后的误码率很小(小于3%),有损压缩就是另一种选择 - 对于这个特定的数据流,位位置的数字权重是无关紧要的,因此高位中的错误是根本不用担心。

提前感谢大家!

5 个答案:

答案 0 :(得分:6)

如果发送第一个未压缩的整数,而对于其他255个整数,则在此整数和前一个整数之间计算XOR,您将获得非零位非常罕见的位流。该比特流可以用Arithmetic coding编码。

如果在计算邻居值之间的XOR之后,我们有一个位流,其中位彼此独立(每个“0”或“1”位具有相同的概率,独立于整数中的位位置并且独立于整数位置。 (数据包),算术编码保证了最佳的无损压缩率。

答案 1 :(得分:5)

您最好的选择是使用现有技术(例如,Lempel-Ziv-Welch; flate)或在这种方法之前使用差异编码(可能更好)。使用差分编码,您将使用该字节与之前的字节之间的差异替换每个字节(第一个除外)。现在你应该得到很多零点,并且散布一些小值。霍夫曼编码或像LZW这样的东西会彻底压缩大部分为零的字符串。

答案 2 :(得分:5)

您可以对输入数据执行XOR。因为只有少数位会发生变化,所以这会为您提供主要由0组成的结果,其中包含一些1

1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  
1100 1001 1110 1010 0001 0101 0110 0101    Sample 2     
1100 1001 1110 1010 0001 0101 0110 0101    Sample 3     
1100 0001 1110 1011 0001 0101 0010 0101    Sample 4     

在起始值之后,这将产生一个序列

0b0000 0000 0000 1000 0000 0000 0001 0000, 
0b0000 0000 0000 0000 0000 0000 0000 0000, 
0b0000 1000 0000 0010 0000 0000 1000 0000

您现在可以使用各种标准压缩算法。霍夫曼编码的8字节序列,LZW或熵编码,但一个很好的尝试可能是一个简单的行长度编码,计算从位位置0的每一位之间的零位:

4, 14, 51, 9, 9

如果您将游程长度限制为30并选择转义符号31,表示“将31添加到下一个游程长度”,则会得到

4, 14, 31, 20, 9, 9

对于整个序列,这将是6 * 5位。您现在可以在 ...

上进行霍夫曼编码

答案 3 :(得分:1)

从你的例子看,似乎改变的几个位并不总是相同的(例如总是最低的4位)。所以我建议对转置数组上的位进行简单的运行长度编码。如果没有数据/数据的分布,我建议从4位开始,但是你可以尝试使用一些示例输入。

伪代码(用于压缩)看起来像这样:

 for bitpos = 0 to 31
     for datapos = 0 to 255 
         BitString.append(getbit(data[datapos], bitpos);
     endfor
 endfor

 result="";
 pos = 0;
 while (notEndOfString)
     # count 1s
     count = 0;
     while (pos < 32*256 AND count < 16 AND BitString[pos]==1)
         count++;
         pos++;
         endwhile
     result.append4BitNumber(count);
     # count 0s
     count = 0;
     while (pos < 32*256 AND count < 16 AND BitString[pos]==0)
         count++;
         pos++;
         endwhile
     result.append4BitNumber(count);
 endwhile

也许有人可以通过应用Lempel-Ziv或Huffman编码来增加压缩 - 但是如果没有关于输入数据分布的更多信息,就不能说更多(这通常适用于这个问题 - 更好的信息输入数据,可以为它量身定制某种压缩方式。)

编辑:另一种简单的方法是对不断变化的位位置进行编码: 从最初的32位字开始,然后为每个数据字存储3位,定义位数变化(即0..7),然后存储0..7乘4位,其中4位编码位置chaning位。这意味着什么时候您需要32 * 256位数据包平均2位更改32 + 255 *(3 + 8)= 2837 =&gt;约为其原始尺寸的35%。

如果你经常改变相同数量的比特,那么这些4比特模式中的一些将经常出现,而其他模式则根本不会出现=&gt;对这4个比特组进行编码的霍夫曼会将其压缩到最优(如果你知道这些模式概率永远不会改变,你甚至可以制作一个静态的霍夫曼树,所以你不必存储它)。

答案 4 :(得分:1)

我的想法类似于Evgeny Kluev。 第一个整数是未压缩的,其余的变为自身的XOR和前一个整数。

1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  
               *                   * 
0000 0000 0000 1000 0000 0000 0000 1000    Sample 2

0000 0000 0000 0000 0000 0000 0000 0000    Sample 3
     *            *            *   
0000 1000 0000 0001 0000 0000 0100 0000    Sample 4

现在不是将稀疏数据分成块而是在这里进行算术编码, 我进一步转换数据。 因为实际上,算术编码是基于不相等的数据的频率。 看着这个,你觉得

0000 0000 0000 1000 0000 0000 0000 1000

会比

更频繁地出现
0000 1000 0000 0001 0000 0000 0100 0000

反之亦然?

好的,这就是我将如何进一步转换数据。 让剩下的数据成为描述数量的数字序列 连续零。 例如,数据变为:

1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  followed by decimals
12, 15, 39, 10, 9, 6

现在,您可以对这些尾随小数执行算术编码。 这次频率有意义! 因为你在问题中说过几乎没有变化,意思 连续的零数会更频繁出现。

编辑:这个答案与hirschhornsalz的答案完全相同。 除了他还提到你可以限制最大零数并将它们分开......