![]() |
1
3
|
![]() |
2
3
“正确答案”即最终算法
解压代码非常短,可以很容易地写成状态机,只有一个指针和一个32位变量给出状态。类似这样:
(代码没有完全按照这种形式进行测试,因此可能会出现拼写错误或其他愚蠢的小错误。) 换档操作有很多重复。我不太担心,因为编译器应该能够清理它。(实际上,左移位可能更好,因为在移位后可以使用进位。但是在C中没有直接的方法可以做到这一点,我不介意。) 另一个优化与字典(查找表)的大小有关。可能有短项和长项,因此可以构建为支持32位、16位或8位项。在这种情况下,必须对字典进行排序,以便小数值指32位项,中间值指16位项,大值指8位项,以避免对齐问题。然后查找代码如下所示:
如果量化级别的数量减少,也可以处理。最简单的情况是4位灰度(1..14)。这需要一个8位状态变量来保持灰度。然后灰度分支将变为:
这实际上允许使用15个中间灰度级(总共17个级别),很好地映射到线性255值系统。
应注意的是,压缩比在某个点开始恶化。三值数据的压缩量不取决于灰度级的数量。灰度数据是未压缩的,八位字节数(几乎)与位数成线性关系。对于典型字体,8位的灰度数据为1/2。。占总数的2/3,但这在很大程度上取决于字体和大小。 因此,从8位减少到4位(在大多数情况下,视觉上很难察觉)通常会将压缩大小减少1/4。。1/3,而降低到三位所提供的进一步减少要少得多。对于这种压缩算法,两位数据没有意义。 如何建立字典?
可以说,我在这个领域缺乏理论知识,因此我认为会有很好的工具提供相当好的近似值。可能有这样的工具,但我找不到,所以我推出了自己的米老鼠版本。 编辑:早期的算法相当愚蠢;发现了一种更简单、更有效的方法
这仍然是一种非常简单的方法,可能会产生非常次优的结果。它唯一的优点是有效。 它工作得怎么样? 一个答案就足够了,但为了详细说明这一点,这里有一些数字。这是一种864字形的字体,典型字形大小为14x11像素,每像素8位。
我们不做任何事情来压缩中间值,因为似乎没有任何有意义的模式。然而,我们用三元数据以明显的幅度击败了熵,甚至数据总量也低于熵极限。所以,我们可以做得更糟。
实际上,较小的字体很难压缩。这并不奇怪,因为大部分信息都在灰度信息中。使用该算法可以有效压缩超大字体,但游程压缩是一种更好的选择。
如果我知道,我会用它!但我仍然可以推测。
不幸的是,这种不可避免的重复在较小尺寸的字体中并不容易利用。我试着创建一个包含所有可能扫描行的字典,然后只引用那些扫描行。不幸的是,不同扫描线的数量很高,因此引用增加的开销超过了好处。如果扫描线本身可以压缩,情况会有所改变,但每个扫描线的八位字节数很小,因此很难进行有效压缩。当然,这个问题取决于字体大小。 我的直觉告诉我,如果使用比全扫描线更长和更短的扫描线,这仍然是正确的方法。只有在有办法创建最佳字典的情况下,结合使用4位像素可能会产生非常好的结果。
这个方向的一个提示是LZMA2压缩文件(带有
|
![]() |
3
1
每个字形的输出是字典中1-N个块的叠加;
|
![]() |
4
1
有损 |
![]() |
5
1
我会选择Clifford的答案,也就是说,首先将字体转换为每像素4位,这足以完成这项任务。 然后,因为这是一种字体,所以有很多行重复,即定义一个字符的行与另一个字符的行匹配。以字母“p”和“b”为例,这些字母的中间部分应该是相同的(如果目标语言使用大量的发音符号,则会有更多的匹配项)。然后,编码器可以首先收集字体的所有不同行,存储这些行,然后每个字符图像由指向这些行的指针列表组成。 效率取决于字体。当然,根据来源,您可能需要一些预处理,以便使用此方法更好地压缩。 如果你想要更多,你可能宁愿选择每像素3位,甚至每像素2位,这取决于你的目标(有些人会手动调整字体图像),这些可能仍然是令人满意的。 总的来说,这种方法对于实时显示效果很好(只需遍历指针即可获得行数据)。 |
![]() |
danial · 如何在多个字符串的每个位置找到最频繁的字符 2 年前 |
![]() |
Manny · 如何比较Perl中的字符串? 2 年前 |
![]() |
Diret · 获取范围内每个数字的子倍数的算法 2 年前 |
![]() |
Saif · 排序时python如何决定何时调用比较器? 2 年前 |