【问题标题】:Better compression algorithm for vector data?更好的矢量数据压缩算法?
【发布时间】:2011-03-08 07:27:48
【问题描述】:

我需要压缩一些空间相关的数据记录。目前我正在使用 zlib 进行 1.2x-1.5x 压缩,但我认为应该有可能获得更多类似 2x 的压缩。数据记录有各种字段,但例如 zlib 似乎无法压缩点列表。

这些点代表一个道路网络。它们是 XXXXYYYY 形式的定点 4 字节整数对。通常,如果单个数据块有 100 个点,那么 X 和 Y 的前两个字节的组合(空间相关性)只会很少。但是底部字节总是在变化,并且对于 zlib 来说必须看起来像随机数据。

同样,记录有 4 字节的 ID,它们往往具有恒定的高字节和可变的低字节。

是否有其他算法可以更好地压缩此类数据?我正在使用 C++。

编辑:请不要再提出更改数据本身的建议。我的问题是关于自动压缩算法。如果有人有所有流行压缩算法概述的链接,我会接受它作为答案。

【问题讨论】:

  • 数据是一系列记录,通常代表道路,每条道路都有一个点列表,以及其他信息,例如地址范围、ID、限速和指向存储的街道名称记录的指针别处。这些被打包成尽可能少的位,诚然,在某些情况下,这也可能会阻碍面向字节的 zlib。
  • 还有一些其他数据是 99% 点,这就是我知道 zlib 没有很好地压缩点的原因。
  • 我有一个压缩实用程序,尚未完成,但可以到达那里。您可以完全访问 (github.com/aunk/comb),它是真正压缩的准备者。如果你能把它记下来,我很乐意让你小有名气。我们可以分享我所追求的奖品。
  • 我的反转字节查找,因此聚集的 0 和 1 的数量被更改为有限数量,以推动实际的压缩程序找到并使用这些模式

标签: c++ compression


【解决方案1】:

如果您尝试根据自己对数据结构的了解来压缩数据,您可能会获得更好的结果。

通用压缩算法只是将您的数据视为比特流。他们寻找常用的位序列,并用较短的字典索引替换它们。

但是重复的数据并没有消失。重复的序列变得更短,但它仍然像以前一样频繁地重复。

据我了解,您有大量表单的数据点

XxxxYYyy,大写字母非常统一。所以把它们排除在外。

将列表重写为类似于以下内容:

XXYY // a header describing the common first and third byte for all the subsequent entries
xxyy // the remaining bytes, which vary
xxyy
xxyy
xxyy
...
XXYY // next unique combination of 1st and 3rd byte)
xxyy
xxyy
...

现在,很少变化的字节的每个组合只列出一次,而不是针对它们出现的每个条目都重复。这样可以节省大量空间。

基本上,在通过 zlib 运行之前尝试自己删除重复数据。您可以做得更好,因为您对数据有更多的了解。

另一种方法可能是,不是将这些坐标存储为绝对数字,而是将它们写为增量,即与选择尽可能接近所有条目的某个位置的相对偏差。您的增量将是较小的数字,可以使用更少的位存储。

【讨论】:

  • 这是个好主意,我只是希望存在一种算法来自动处理这种数据。我的系统设计不太适合这种重新排列。
  • 如果你的系统设计不支持这种重新排列,但支持通过 zlib 运行这些东西,那么只需编写你自己的过滤器来代替 zlib,这是特定于你的数据格式的.
  • @Novelocrat:也是一个好主意,除了我的数据结构虽然是二进制的,但与 XML 一样通用,并且由于各种原因,你的想法对我不起作用......也许 BWT 会有帮助,但我已经放弃了这个问题,所以我接受了这个答案。
【解决方案2】:

不特定于您的数据,但如果可以的话,我建议您使用 7zip 而不是 zlib。我已经看到使用它的压缩率非常好。

http://www.7-zip.org/

【讨论】:

  • +1 表示这个想法(假设 7-zip 可以以源代码形式使用),尽管我没有提到它在 WinCE 上运行并且必须很快; 7-zip 可能太大太慢了。
【解决方案3】:

在没有看到数据及其确切分布的情况下,我无法确定最好的方法是什么,但我建议您以 8 位指示以下内容的字节开始每组 1-4 条记录:

0-1 应从上一条记录中借用的 ID 字节数 2-4 位置记录格式 6-7 使用相同'mode'字节的后续记录数

每个位置记录可以存储八种方式之一;除 000 以外的所有类型都使用有符号位移。位码后面的数字就是位置记录的大小。

000 - 8 - 两个完整的四字节位置 001 - 3 - X 和 Y 的十二位 010 - 2 - 十位 X 和六位 Y 011 - 2 - 六位 X 和十位 Y 100 - 4 - 两个 16 位有符号位移 101 - 3 - 16 位 X 和 8 位 Y 有符号位移 110 - 3 - X 的八位有符号位移; Y 为 16 位 111 - 2 - 两个八位有符号位移

模式字节为零将存储适用于一个点的所有信息,而不参考任何先前的点,总共使用 13 个字节来存储 12 个字节的有用信息。其他模式字节将允许基于与先前记录的相似性来压缩记录。如果四个连续记录仅在 ID 的最后一位不同,并且 X 和 Y 都在前一个记录的 +/- 127 范围内,或者 X 在 +/- 31 范围内,Y 在 +/- 511 范围内,或者 X 在+/- 511 和 +/- 31 内的 Y,则所有四个记录可以存储在 13 个字节中(平均每个 3.25 个字节(空间减少 73%)。

可以使用“贪婪”算法进行压缩:检查记录以查看它必须在输出中使用什么大小的 ID 和 XY,然后再抓取最多三个记录,直到找到一个记录都不能使用选择的大小与先前的记录“匹配”,或者可以写得更小(请注意,例如,如果第一条记录的 X 和 Y 位移都等于 12,则 XY 将用两个字节写入,但直到读取以下记录不知道要使用这三种两字节格式中的哪一种)。

在确定您的格式之前,我建议您通过它运行您的数据。可能是一个小的调整(例如,使用 7+9 或 5+11 位格式而不是 6+10)可以让许多数据更好地打包。但是,唯一真正了解的方法是查看您的真实数据会发生什么。

【讨论】:

    【解决方案4】:

    看起来Burrows–Wheeler transform 可能对这个问题有用。它有一种将重复字节放在一起的特殊趋势,这可能会使 zlib 压缩得更好。 This article 建议我应该将 zlib 以外的其他算法与 BWT 结合起来。

    直觉上这听起来很昂贵,但查看一些源代码表明反向 BWT 是 O(N),需要 3 次数据传递和适度的空间开销,可能使其在我的目标平台 (WinCE) 上足够快。假设一个普通的排序算法,前向变换大约为 O(N log N) 或略高于。

    【讨论】:

    • 这是我的建议。具体来说,我建议您查看 BZip2,它是我所知道的唯一流行的 BWT 实现。它有一组压缩器,可能不适合您的数据集,但具有很好的调试和指定的优点。
    【解决方案5】:

    通过某种邻近度度量对点进行排序,使得相邻点之间的平均距离很小。然后存储相邻点之间的差异。

    如果您设法对点进行排序,使 x 轴和 y 轴上的大多数差异都是正数,您可能会做得更好,但我不能肯定。

    作为 zlib 的替代方案,当概率分布偏向小数字时,可以很好地工作的一系列压缩技术是 universal codes。它们必须针对签名数字进行调整(编码abs(x)<<1 + (x < 0 ? 1 : 0))。

    【讨论】:

    • 积分是有序的。如果我更改了它们的顺序,那么我将不得不添加一个索引来重建原始顺序,这可能会增加整体大小。包含这些点的记录(表示道路)是无序的,但重新排列它们是一个 PITA。我怀疑我可以通过分离高字节和低字节来获得更好的压缩,但是由于我的数据存储系统是高度通用的(用于系统中的所有记录,无论它们的性质如何),我认为最好避免用特殊的方式污染它-目的代码。
    • 另外,我不认为在空间上安排它们会对 zlib 有多大帮助。仅根据我对 deflate 算法的了解,我认为它不能识别数字相关性 - 如果你给它一个字节序列 1、2、3、... 100,它的压缩率和随机字节一样严重。
    • @Qwertie:考虑两个具有绝对值的序列:100,101,102 和 200,201,202。现在将它们视为相对值:0,1,2 和 0,1,2。去相对允许任何基于字典的压缩器将这两者结合起来。
    • 好点,尽管在地理点的情况下,点对/点系列之间的差异对于不同的点对/系列来说几乎从不完全相同 - 但确实,它们会比原始点更相似点。
    • 它们不必完全相同。因为 delta 的概率分布严重偏向于小数字,所以霍夫曼树会通过需要更少的比特来编码它们来支持这些。不过,请参阅我修改后的答案。
    【解决方案6】:

    您可能希望将两个列表写入压缩文件:NodeListLinkList。每个节点都有一个 ID,x,y。每个链接都有一个 FromNode 和一个 ToNode,以及一个中间 xy 值列表。您可能会有一个带有错误来源的标头记录,并具有与之相关的节点 xy 值。

    如果您的街道遵循城市网格网络,通过消除交叉路口的重复坐标,这将提供最大的好处。

    如果压缩不需要无损,您可以使用截断增量作为中间坐标。虽然上面有人提到了三角洲,但请记住,连通性的损失可能会比形状的损失造成更多的问题,如果你使用截断的三角洲来表示道路的最后一个坐标,就会发生这种情况(通常是十字路口)。

    同样,如果您的道路不在城市电网上,这可能不会给您带来太多收益。

    【讨论】:

    • 我只是在上面的评论中注意到“和其他信息,例如地址范围、ID、速度限制和指向存储在其他地方的街道名称记录的指针”。如果您有很多属性,您可能会查看是否有可用的线性参考系统 LRS(例如 roadID、里程碑)。这将允许您让一个属性记录跨越多个路段,而不是为每个路段重复。 en.wikipedia.org/wiki/Linear_Reference_System
    • 谢谢,但我只是想知道压缩算法。您的建议是对我最终可能会考虑的数据进行完全重组(但我已经对如何做到这一点有了自己的想法)。顺便说一句,想想看,只要重复点是同一数据“块”的一部分,重复点可能不是什么大问题,因为 zlib 应该注意到重复并将其压缩出来。
    • 想一想,LRS 很可能会派上用场……最终。谢谢你的提示!我需要完全重写很多东西,但我们可能只有预算;)
    猜你喜欢
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多