嵌入式使用的轻量级(de)压缩算法

时间:2017-08-26 22:36:33

标签: algorithm embedded compression

我有一个带有图形用户界面的低资源嵌入式系统。界面需要字体数据。为了保存只读存储器(闪存),需要压缩字体数据。我正在寻找一种用于此目的的算法。

要压缩的数据的属性

  • 每像素8位的矩形像素图的透明度数据
  • 字体中通常有大约200..300个字形(以特定大小采样的字体)
  • 每个字形的大小通常为6x9到15x20像素
  • 有很多零(“没有墨水”)和少一些255(“完全墨水”),否则由于抗锯齿的性质,八位字节的分布非常均匀

压缩算法的要求

  • 解压缩算法的重要指标是数据的大小加上算法的大小(因为它们将驻留在相同的有限内存中)。
  • 可用于解压缩的RAM很少;可以将单个字形的数据解压缩到RAM中,但不能多。
  • 为了使事情变得更加困难,算法必须在32位微控制器(ARM Cortex-M内核)上非常快,因为字形在被绘制到显示器上时需要解压缩。每个八位字节可以运行十到二十个机器周期,百分之一肯定太多了。
  • 为了方便起见,完整的数据语料库是先验已知的,并且在压缩阶段有很多处理能力和可用内存。

结论和想法

  • 由于相对较高的熵,通过一些可变长度编码打包每个八位字节的天真方法不能提供良好的结果。
  • 任何利用先前解压缩数据的算法似乎都是不可能的,因为无法存储其他字形的解压缩数据。这使得LZ算法效率较低,因为它们只能引用少量数据。
  • 对处理能力的限制似乎排除了大多数按位操作,即解压缩应该处理八位字节的数据。这使得霍夫曼编码变得困难,并且算术编码变得不可能。
  • 问题似乎是静态字典编码的一个很好的选择,因为所有数据都是事先已知的,而且数据本质上有些重复(不同的字形共享相同的形状)。

问题

  • 如何构建好词典?我知道找到某些数据的最佳字典是一个完整的np问题,但有没有合理的近似值?我已经尝试了zstandard的字典构建器,但结果不是很好。
  • 我的结论中有什么东西我错了吗? (我是在错误的轨道上而忽略了一些明显的东西吗?)

目前最好的算法

为了给出一些背景信息,我能够弄清楚的最有用的算法如下:

  • 单个字形的字体数据中的所有样本被连接(展平)为一维数组(向量,表格)。
  • 每个样本都有三种可能的状态:0,255和“其他东西”。
  • 此信息一次打包五个连续样本,分为5位数三位数(0..3 ^ 5)。
  • 由于八位字节中有一些额外的值(2 ^ 8 = 256,3 ^ 5 = 243),它们用于表示0和255的较长字符串。
  • 对于每个“别的”值,实际值(1..254)存储在单独的矢量中。

此数据可以快速解压缩,因为base-3值可以通过一个小的(243 x 3 = 729个八位字节)查找表解码为base-4值。压缩比高度依赖于字体大小,但是根据我的典型数据,我可以得到1:2左右。由于这比LZ变体(大约1:3)差得多,我想尝试静态字典方法。

当然,通常的LZ变体使用霍夫曼或算术编码,这自然会使压缩数据变小。另一方面,我有所有可用的数据,压缩速度不是问题。这应该可以找到更好的词典。

由于数据的性质,我可以使用有损算法,但在这种情况下,最可能的有损算法将减少像素数据中的量化级别的数量。这不会对基础压缩问题产生太大影响,我希望避免产生比特对齐的麻烦。

4 个答案:

答案 0 :(得分:3)

答案 1 :(得分:1)

您可以使用带有自定义词典的稀疏表示来尝试有损压缩。

每个字形的输出是字典中1-N块的叠加;

  • 预处理花费的大部分cpu时间
  • 每像素的预定解码时间(最大,平均或常数N)加法
  • 可控制的压缩大小(字典大小+ x y 每个字形的n个代码)

答案 2 :(得分:1)

似乎最简单的有损方法是减少每像素位数。对于这种大小的字形,16个级别可能就足够了。这会立即将数据减半,然后您可以将现有算法应用于值0,16或“其他内容”,或者将其再次减半。

答案 3 :(得分:1)

我会选择克利福德的答案,即首先将字体转换为每像素4位,足以完成此任务。

然后,因为这是一种字体,所以你有很多行重复,即定义一个字符的行与另一个字符的行匹配。以例如字母' p'并且' b',这些字母的中间部分应该是相同的(如果目标语言使用负载变音符号,您将获得更多匹配)。然后,您的编码器可以先收集字体的所有不同行,存储这些行,然后每个字符图像由指向行的指针列表组成。

效率取决于字体当然,取决于来源,您可能需要一些预处理来使用此方法更好地压缩。

如果你想要更多,你可能宁愿选择每像素3位甚至每像素2位,这取决于你的目标(以及一些用于手动调整字体图像的方法),这些可能仍然令人满意。 / p>

此方法总体上当然适用于实时显示(您只需要遍历指针以获取行数据)。