遥感影像免切片4:LZW 压缩算法

342 阅读7分钟

LZW 压缩算法英文全称 Lempel-Ziv-Welch Encoding,该算法是由 Lempel、Ziv、Welch 三人发明,它基于表查询算法删除重复数据从而实现减小文件大小的无损压缩算法,也称为“串表压缩算法”,主要用于 TIFF、GIF、PDF 文件压缩以及文字压缩。LZW 压缩算法实现简单、通用性强并且在硬件实现中具有高吞吐量,通常在许多 PC 应用程序都将它作为通用数据压缩的方式,也是 Unix 操作系统文件压缩程序的一部分。

1. 基本原理

LZW 压缩算法的基本原理:通过建立一个字典表,用较短的代码表示较长的字符串,从而实现对数据的无损压缩。详细点说,LZW 算法提取原始文件数据中的不同字符,基于这些字符创建一个字典表,然后用字典表中的字符索引代替原始文件数据中相应的字符,从而实现对原始文件数据的无损压缩。

2. 编解码流程

对原始数据TOBEORNOTTOBEORTOBEORNOT进行编解码演示,假设采用 ASCII 编码规则,以 ASCII 码表作为初始字典表。ASCII 码表十进制范围为0~255,详细内容可点击这里查看,简单描述可查看附录

2.1 编码流程演示

编码过程中构造的字典的 Key 为 ASCII 码、Value 为 ASCII 值

程序输入TOBEORNOTTOBEORTOBEORNOT、输出[84, 79, 66, 69, 79, 82, 78, 79, 84, 256, 258, 260, 265, 259, 261, 263]
输入的字符共包含24个字符,在 Java 程序中,每个字符占用2个字节、16位,共占用384位。输出结果中最大值为263,可以用9位的二进制值表示,共占用16 * 9 = 144位,压缩比为2.67。

步骤已识别字符Ω当前字符λΩ+λΩ+λ是否在字典中操作输出
1(空字符串)TTY(84)Ω=Ω+λ(T)
2TOTON字典新增TO=256;
Ω=λ(O);
输出结果增加84
84
3OBOBN字典新增OB=257;
Ω=λ(B);
输出结果增加79
84、79
4BEBEN字典新增BE=258;
Ω=λ(E);
输出结果增加66
84、79、66
5EOEON字典新增EO=259;
Ω=λ(O);
输出结果增加69
84、79、66、69
6ORORN字典新增OR=260;
Ω=λ(R);
输出结果增加79
84、79、66、69、79
7RNRNN字典新增RN=261;
Ω=λ(N);
输出结果增加82
84、79、66、69、79、82
8NONON字典新增NO=262;
Ω=λ(O);
输出结果增加78
84、79、66、69、79、82、78
9OTOTN字典新增OT=263;
Ω=λ(T);
输出结果增加79
84、79、66、69、79、82、78、79
10TTTTN字典新增TT=264;
Ω=λ(T);
输出结果增加84
84、79、66、69、79、82、78、79、84
11TOTOY(256)Ω=Ω+λ(TO)
12TOBTOBN字典新增TOB=265;
Ω=λ(B);
输出结果增加256
84、79、66、69、79、82、78、79、84、256
13BEBEY(258)Ω=Ω+λ(BE)
14BEOBEON字典新增BEO=266;
Ω=λ(O);
输出结果增加258
84、79、66、69、79、82、78、79、84、256、258
15ORORY(260)Ω=Ω+λ(OR)
16ORTORTN字典新增ORT=267;
Ω=λ(T);
输出结果增加260
84、79、66、69、79、82、78、79、84、256、258、260
17TOTOY(256)Ω=Ω+λ(TO)
18TOBTOBY(265)Ω=Ω+λ(TOB)
19TOBETOBEN字典新增TOBE=268;
Ω=λ(E);
输出结果增加265
84、79、66、69、79、82、78、79、84、256、258、260、265
20EOEOY(259)Ω=Ω+λ(EO)
21EOREORN字典新增EOR=269;
Ω=λ(R);
输出结果增加259
84、79、66、69、79、82、78、79、84、256、258、260、265、259
22RNRNY(261)Ω=Ω+λ(RN)
23RNORNON字典新增RNO=270;
Ω=λ(O);
输出结果增加261
84、79、66、69、79、82、78、79、84、256、258、260、265、259、261
24OTOTY(263)Ω=Ω+λ(OT)
25OT(空字符串)OTY(263)输出结果增加26384、79、66、69、79、82、78、79、84、256、258、260、265、259、261、263

2.2 解码流程演示

解码构造的字典与编码构造的字典有所不同,其 Key 为 ASCII 值、Value 为 ASCII 码

程序输入[84, 79, 66, 69, 79, 82, 78, 79, 84, 256, 258, 260, 265, 259, 261, 263]、输出TOBEORNOTTOBEORTOBEORNOT

步骤前一个被解码的字符Ω当前读入的字符λλ是否在字典中操作输出
1(空字符串)84第一个字符肯定在字典中,直接输出Ω=λ(T);
输出结果增加T
T
2T79Y(O)字典新增256=Ωλ<第1个字符>(TO);
Ω=λ(O);
输出结果增加O
TO
3O66Y(B)字典新增257=Ωλ<第1个字符>(OB);
Ω=λ(B);
输出结果增加B
TOB
4B69Y(E)字典新增258=Ωλ<第1个字符>(BE);
Ω=λ(E);
输出结果增加E
TOBE
5E79Y(O)字典新增259=Ωλ<第1个字符>(EO);
Ω=λ(O);
输出结果增加O
TOBEO
6O82Y(R)字典新增260=Ωλ<第1个字符>(OR);
Ω=λ(R);
输出结果增加R
TOBEOR
7R78Y(N)字典新增261=Ωλ<第1个字符>(RN);
Ω=λ(N);
输出结果增加N
TOBEORN
8N79Y(O)字典新增262=Ωλ<第1个字符>(NO);
Ω=λ(O);
输出结果增加O
TOBEORNO
9O84Y(T)字典新增263=Ωλ<第1个字符>(OT);
Ω=λ(T);
输出结果增加T
TOBEORNOT
10T256Y(TO)字典新增264=Ωλ<第1个字符>(TT);
Ω=λ(TO);
输出结果增加TO
TOBEORNOTTO
11TO258Y(BE)字典新增265=Ωλ<第1个字符>(TOB);
Ω=λ(BE);
输出结果增加BE
TOBEORNOTTOBE
12BE260Y(OR)字典新增266=Ωλ<第1个字符>(BEO);
Ω=λ(OR);
输出结果增加OR
TOBEORNOTTOBEOR
13OR265Y(TOB)字典新增267=Ωλ<第1个字符>(ORT);
Ω=λ(BE);
输出结果增加TOB
TOBEORNOTTOBEORTOB
14BE259Y(EO)字典新增268=Ωλ<第1个字符>(BEE);
Ω=λ(EO);
输出结果增加EO
TOBEORNOTTOBEORTOBEO
15EO261Y(RN)字典新增269=Ωλ<第1个字符>(EOR);
Ω=λ(RN);
输出结果增加RN
TOBEORNOTTOBEORTOBEORN
16RN263Y(OT)字典新增270=Ωλ<第1个字符>(RNO);
Ω=λ(OT);
输出结果增加OT
TOBEORNOTTOBEORTOBEORNOT

3. 代码实现

DICT_SIZE = 256

3.1 编码

 public static List<Integer> encoder(String charStream) {
    // 初始化字典
    Map<String, Integer> dictionary = new HashMap<>();
    for (int i = 0; i < DICT_SIZE; i++) {
        dictionary.put(String.valueOf((char) i), i);
    }

    // 压缩数据
    List<Integer> result = new ArrayList<>();
    String foundCharacters = "";
    for (char character : charStream.toCharArray()) {
        String charactersToAdd = foundCharacters + character;

        if (dictionary.containsKey(charactersToAdd)) {
            foundCharacters = charactersToAdd;
        } else {
            result.add(dictionary.get(foundCharacters));
            dictionary.put(charactersToAdd, dictionary.size());
            foundCharacters = String.valueOf(character);
        }
    }
    if (!foundCharacters.isEmpty()) {
        result.add(dictionary.get(foundCharacters));
    }

    return result;
}

3.2 解码

public static String decode(List<Integer> codeStream) {
    // 初始化字典
    Map<Integer, String> dictionary = new HashMap<>();
    for (int i = 0; i < DICT_SIZE; i++) {
        dictionary.put(i, String.valueOf((char) i));
    }

    // 解压数据
    String characters = String.valueOf((char) codeStream.remove(0).intValue());
    StringBuilder result = new StringBuilder(characters);
    for (int code : codeStream) {
        String entry = dictionary.containsKey(code) ? dictionary.get(code) : characters + characters.charAt(0);
        result.append(entry);
        dictionary.put(dictionary.size(), characters + entry.charAt(0));
        characters = entry;
    }
    return result.toString();
}

附录:ACSII 码表

  1. 第0-31位表示控制字符

image.png

  1. 第32-127位表示打印字符

image.png

  1. 第128-255位表示扩展字符

image.png