我正在压缩由数据包组成的二进制流
数据包由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%),有损压缩就是另一种选择 - 对于这个特定的数据流,位位置的数字权重是无关紧要的,因此高位中的错误是根本不用担心。
提前感谢大家!
答案 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的答案完全相同。 除了他还提到你可以限制最大零数并将它们分开......