熵编码(算术+霍夫曼)编解码基础知识总结

article/2025/9/30 8:15:03

在MPEG的TMC13模型中,对于surface point cloud compression,对block和vertices进行熵编码;对于lidar point cloud compression,需要对量化残差进行算术编码。这里对熵编码相关的知识进行了总结。


熵编码:

(1)https://blog.csdn.net/m0_37579288/article/details/79552526  (2)https://zhuanlan.zhihu.com/p/26454072

信息论中,Shannon发明了信息熵,信息熵是信息量的度量单位,信息学里的熵表示数据的无序度,熵越高,则包含的信息越多。

熵编码是一种无损编码方式。最常见熵编码有霍夫曼(Huffman)编码,算术编码,还有行程编码 (RLE)、基于上下文的自适应变长编码(CAVLC)、基于上下文的自适应二进制算术编码(CABAC)。

在图像(视频)压缩和点云压缩中经常用到熵编码,譬如,JPEG用的是Huffman和算术编码,而CAVLC、CABAC是HEVC中使用的两种编码方式。


算术编码:

原理解析:https://blog.csdn.net/u010029439/article/details/104147157

模拟算数编码步骤:https://blog.csdn.net/lkxiaolou/article/details/8781024

算数编码实验:https://blog.csdn.net/CSDNJay/article/details/46628603 MATLAB代码

算术编码和其他熵编码方式的不同在于,其他的熵编码通常是把输入的消息分割为符号,对每个符号进行编码,而算术编码是直接吧整个输入的消息编码为一个数,小数介于 [ 0.0 , 1.0 ) 区间。其大体过程为:

  1. 假设有一段数据需要编码,统计里面所有的字符和出现的次数。
  2. 将区间 [0,1) 连续划分成多个子区间,每个子区间代表一个上述字符, 区间的大小正比于这个字符在文中出现的概率 p。概率越大,则区间越大。所有的子区间加起来正好是 [0,1)。
  3. 编码从一个初始区间 [0,1) 开始,设置:,
  4. 不断读入原始数据的字符,找到这个字符所在的区间,比如 [ LH ),更新:
  5. 最后将得到的区间 [low, high)中任意一个小数以二进制形式输出即得到编码的数据。

举例说明:

(1)有一段原始数据“ARBER”,他们出现的概率和次数如下:

SymbolTimesP
A10.2
B10.2
E10.2
R20.4

(2)将这几个字符的区间在 [0,1) 上按照概率大小连续一字排开,我们得到一个划分好的 [0,1)区间:
图片描述

(3)开始编码,初始区间是 [0,1)。注意这里又用了区间这个词,不过这个区间不同于上面代表各个字符的概率区间 [0,1)。这里我们可以称之为编码区间,这个区间是会变化的,确切来说是不断变小。我们将编码过程用下图完整地表示出来:
图片描述

可以看出编码区间逐渐变小。每次编码一个字符,就在现有的编码区间上,按照概率比例取出这个字符对应的子区间。例如一开始A落在0到0.2上,因此编码区间缩小为 [0,0.2),第二个字符是R,则在 [0,0.2)上按比例取出R对应的子区间 [0.12,0.2),以此类推。每次得到的新的区间都能精确无误地确定当前字符,并且保留了之前所有字符的信息,因为新的编码区间永远是在之前的子区间。最后我们会得到一个长长的小数,这个小数神奇地包含了所有的原始数据,不得不说这真是一种非常精彩的思想。

(4)解码:如果你理解了编码的原理,则解码的方法显而易见,就是编码过程的逆推。从编码得到的小数开始,不断地寻找小数落在了哪个概率区间,就能将原来的字符一个个地找出来。例如得到的小数是0.14432,则第一个字符显然是A,因为它落在了 [0,0.2)上,接下来再看0.14432落在了 [0,0.2)区间的哪一个相对子区间,发现是 [0.6,1), 就能找到第二个字符是R,依此类推。在这里就不赘述解码的具体步骤了。

代码:MATLAB

https://www.zhihu.com/question/32301828  介绍function cumsum()

涉及的MATLAB函数:cumsum()    find()    vpa()

%%算术编码
clc; 
a=['_','a','e','r','s','t']; 
s=input('字符串'); 
space=0;a1=0;e1=0;r1=0;s1=0;t1=0; 
for i=1:length(s)c=find(a==s(i));switch c case 1 space=space+1; case 2 a1=a1+1; case 3 e1=e1+1; case 4r1=r1+1; case 5s1=s1+1;case 6t1=t1+1;end 
end
disp('_,a,e,r,s,t概率分别是');
p=[space,a1,e1,r1,s1,t1]/length(s) 
sp=cumsum(p);  %cumsum累计求和,sp=a1,a1+a2,a1+a2+a3,...
low=0;high=1;
range_length=high-low; 
for i=1:length(s) c=find(a==s(i)); %find函数用来定位High=sp(c);Low=High-p(c);next_low=low+range_length*Low; next_high=low+range_length*High;low=next_low;high=next_high;range_length=high-low; disp([vpa(low,11),vpa(high,11)]) %vpa用于精度控制
end

在命令行窗口输入‘state_tree’,(注意加单引号),结果如下:

 

clc;
format long
p=[0.1,0.1,0.3,0.1,0.1,0.3];
s='_aerst';
high=cumsum(p);
low=high-p;
number=input('输入数值');
while(number)c=find((number>=low)&(number<high));disp(s(c));range_low=low(c);range=p(c);number_next=vpa(((number-range_low)/range),11);number=double(number_next);
end

 输出是state_treatttt.....由于取了精度,最后的结果会有误差。


霍夫曼编码:

(1)https://www.cnblogs.com/Jezze/archive/2011/12/23/2299884.html 哈夫曼编码与哈夫曼树

(2)https://blog.csdn.net/u014106644/article/details/90050977 霍夫曼编码原理和Java代码

(3)https://blog.csdn.net/xgf415/article/details/52628073 霍夫曼编码很详细的步骤,好评鸭

霍夫曼编码的具体步骤如下:

  1. 将信源符号的概率按减小的顺序排队。
  2. 把两个最小的概率相加,并继续这一步骤,始终将较高的概率分支放在右边,直到最后变成概率1。
  3. 画出由概率1处到每个信源符号的路径,顺序记下沿路径的0和1,所得就是该符号的霍夫曼码字。   
  4. 将每对组合的左边一个指定为0,右边一个指定为1(或相反)。

例如给定一段数据,统计里面字符的出现次数,生成哈夫曼树,我们可以得到字符编码集:

SymbolTimesEncoding
a300
b301
c210
d1110
e2111

图片描述

仔细观察编码所表示的小数,从0.0到0.111,其实就是构成了算数编码中的各个概率区间,并且概率越大,所用的bit数越少,区间则反而越大。概率越小的字符,用更多的bit去表示,这反映到概率区间上就是,概率小的字符所对应的区间也小,因此这个区间的上下边际值的差值越小,为了唯一确定当前这个区间,则需要更多的数字去表示它。我们仍以十进制来说明,例如大区间0.2到0.3,我们需要0.2来确定,一位足以表示;但如果是小的区间0.11112到0.11113,则需要0.11112才能确定这个区间,编码时就需要5位才能将这个字符确定。

哈夫曼编码的不同之处就在于,它所划分出来的子区间并不是严格按照概率的大小等比例划分的(节点的左右子树按照0.5/0.5的概率划分)。例如上面的d和e,概率其实是不同的,但却得到了相同的子区间大小0.125。可以将哈夫曼编码和算术编码在这个例子里的概率区间做个对比:

图片描述

代码:C语言

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>#define MAXBIT      100
#define MAXVALUE  10000
#define MAXLEAF     30
#define MAXNODE    MAXLEAF*2 -1typedef struct
{int bit[MAXBIT];int start;
} HCodeType;        /* 编码结构体 */typedef struct
{int weight;int parent;int lchild;int rchild;char value;
} HNodeType;        /* 结点结构体 *//* 构造一颗哈夫曼树 */
void HuffmanTree(HNodeType HuffNode[MAXNODE], int n)
{/* i、j: 循环变量,m1、m2:构造哈夫曼树不同过程中两个最小权值结点的权值,x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号。*/int i, j, m1, m2, x1, x2;/* 初始化存放哈夫曼树数组 HuffNode[] 中的结点 */for (i = 0; i < 2 * n - 1; i++){HuffNode[i].weight = 0;//权值 HuffNode[i].parent = -1;HuffNode[i].lchild = -1;HuffNode[i].rchild = -1;HuffNode[i].value = ' '; //实际值,可根据情况替换为字母  } /* end for *//* 输入 n 个叶子结点的权值 */for (i = 0; i < n; i++){printf("Please input char of leaf node: ", i);scanf("%c", &HuffNode[i].value);getchar();} /* end for */for (i = 0; i < n; i++){printf("Please input  weight of leaf node: ", i);scanf("%d", &HuffNode[i].weight);getchar();} /* end for *//* 构循环造 Huffman 树 */for (i = 0; i < n - 1; i++){m1 = m2 = MAXVALUE;     /* m1、m2中存放两个无父结点且结点权值最小的两个结点 */x1 = x2 = 0;/* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */for (j = 0; j < n + i; j++){if (HuffNode[j].weight < m1 && HuffNode[j].parent == -1){m2 = m1;x2 = x1;m1 = HuffNode[j].weight;x1 = j;}else if (HuffNode[j].weight < m2 && HuffNode[j].parent == -1){m2 = HuffNode[j].weight;x2 = j;}} /* end for *//* 设置找到的两个子结点 x1、x2 的父结点信息 */HuffNode[x1].parent = n + i;HuffNode[x2].parent = n + i;HuffNode[n + i].weight = HuffNode[x1].weight + HuffNode[x2].weight;HuffNode[n + i].lchild = x1;HuffNode[n + i].rchild = x2;printf("x1.weight and x2.weight in round %d: %d, %d\n", i + 1, HuffNode[x1].weight, HuffNode[x2].weight);  /* 用于测试 */printf("\n");} /* end for */
} /* end HuffmanTree *///解码 
void decodeing(char string[], HNodeType Buf[], int Num)
{int i, tmp = 0, code[1024];int m = 2 * Num - 1;char *nump;char num[1024];for (i = 0; i < strlen(string); i++){if (string[i] == '0')num[i] = 0;elsenum[i] = 1;}i = 0;nump = &num[0];while (nump < (&num[strlen(string)])){tmp = m - 1;while ((Buf[tmp].lchild != -1) && (Buf[tmp].rchild != -1)){if (*nump == 0){tmp = Buf[tmp].lchild;}else tmp = Buf[tmp].rchild;nump++;}//	printf("123");printf("字符编号是:%d", tmp);printf("%c", Buf[tmp].value);//这里一直显示不出来}
}int main(void)
{HNodeType HuffNode[MAXNODE];            /* 定义一个结点结构体数组 */HCodeType HuffCode[MAXLEAF], cd;       /* 定义一个编码结构体数组, 同时定义一个临时变量来存放求解编码时的信息 */int i, j, c, p, n;char pp[100];printf("Please input n:\n");scanf("%d", &n);HuffmanTree(HuffNode, n);for (i = 0; i < n; i++){cd.start = n - 1;c = i;p = HuffNode[c].parent;while (p != -1)   /* 父结点存在 */{if (HuffNode[p].lchild == c)cd.bit[cd.start] = 0;elsecd.bit[cd.start] = 1;cd.start--;        /* 求编码的低一位 */c = p;p = HuffNode[c].parent;    /* 设置下一循环条件 */} /* end while *//* 保存求出的每个叶结点的哈夫曼编码和编码的起始位 */for (j = cd.start + 1; j < n; j++){HuffCode[i].bit[j] = cd.bit[j];}HuffCode[i].start = cd.start;} /* end for *//* 输出已保存好的所有存在编码的哈夫曼编码 */for (i = 0; i < n; i++){printf("%d 's Huffman code is: ", i);for (j = HuffCode[i].start + 1; j < n; j++){printf("%d", HuffCode[i].bit[j]);}printf(" start:%d", HuffCode[i].start);printf("\n");}printf("Decoding,Please Enter 0/1 code:\n");scanf("%s", &pp);decodeing(pp, HuffNode, n);getchar();return 0;
}

实验结果如下:【疑问】本来是输入0/1编码后,直接输出解码的结果,但解码部分最后一步printf("%c", Buf[tmp].value);不知为何一直显示不了,我看了好久,也不知道哪里有问题,枯了,请各位路过的道友知道怎么改的烦请在评论区纠正一下,谢谢啦~


ε=(´ο`*)))唉,做菜鸡好难,每天只会ctrl+c、ctrl+v,啥也不是。


http://chatgpt.dhexx.cn/article/HbBxxLmd.shtml

相关文章

2 熵与编码

先来尝试编码一副扑克牌&#xff0c;首先考虑花色rank的方式编码&#xff0c;如下图&#xff0c;即第一张牌是0&#xff0c;最后一张是51&#xff08;一共52张牌&#xff09; 在一个集合中&#xff0c;假设最大元素为M&#xff0c;那么我们对M编码需要的最小编码长度为log2M&a…

编码原理详解(五)---熵编码(CAVAL)

上一篇我们讲到了ZigZag扫描&#xff0c;经过这一扫描之后&#xff0c;发现原本是4*4的像素矩阵&#xff0c;就变成了一连串的数字&#xff0c;可以说是二维到一维的一个转换吧&#xff0c;而且经过ZigZag扫描后&#xff0c;一连串的数字的最后大部分为0&#xff0c;以及一些1,…

信息熵与编码定理

惊奇度与信息量 定性描述 惊奇度&#xff1a;一个事件的惊奇度是指该事件发生时我们所感到的惊奇程度 信息量&#xff1a;一条信息的信息量是指该信息所含信息的多少。一条信息越是让我们感到惊奇&#xff0c;它所含信息量就越大 对于一个掷骰子的试验&#xff0c;假设E代表掷…

熵编码算法Range encoding工程原理和实现

在压缩算法中&#xff0c;熵编码是其中重要的无损压缩步骤。熵编码算法根据香农定理&#xff0c;对出现概率大的源符号用较少的编码符号进行编码&#xff0c;对概率小的源符号用较多的编码符号进行编码&#xff0c;尽可能地逼近压缩的极限。 目前各类压缩工具使用的熵编码算法主…

七、熵编码算法(1):基础知识

一、熵编码的概念 熵 化学和热力学&#xff0c;用于度量能量退化的指标熵越高&#xff0c;物体或系统的做功能力越低 信息学中的熵 表示信源所发出信息的不确定性越是随机的、前后不相关的信息&#xff0c;其熵越高 信源编码定理 说明了香农熵与信源符号概率之间的关系信息的熵…

【Codecs系列】CABAC熵编码详解

Date: 2018.5.9 转载自&#xff1a;https://blog.csdn.net/listener51/article/details/60970635 目录 1. 信息熵的概念 &#xff12;. 定长编码 &#xff13;. 变长编码 3.1 哈夫曼编码 3.2 算术编码  3.2.1 传统编码方法 3.2.2 算术编码 3.2.3 二进制算术编码 4. …

第8章 熵编码

http://www.cnblogs.com/xkfz007/archive/2012/07/29/2614250.html 1. 熵编码 熵&#xff08;Entropy&#xff09;&#xff1a;信源的平均信息量&#xff0c;更精确的描述为表示信源所有符号包含信息的平均比特数 信源编码要尽可能的减少信源的冗余&#xff0c;使之接近熵 用…

熵编码之CABAC

CABAC&#xff08;Context-based Adaptive Binary Arithmetic Coding&#xff09;&#xff0c;基于上下文的自适应二进制算术编码。CABAC是H.264/AVC标准中两种熵编码中的一种&#xff0c;它的编码核心算法就是算术编码&#xff08;Arithmetic Coding&#xff09;。 算术编码 传…

信息熵、编码冗余/信息熵冗余、压缩与解压缩速度

信息熵&#xff1a;是指数据所带的信息量。信息量与信源包含的事件发生的概率有关&#xff0c;事件概率越大&#xff0c;信息量越小&#xff1b;事件概率越小&#xff0c;信息量越大。将信源所有可能事件的信息量进行平均&#xff0c;就得到信息的熵&#xff08;Entropy&#x…

信息熵和压缩编码

目录 一、信息熵是什么&#xff1f;二、两种编码压缩2.1 香农-范诺编码简述2.2 特例详解 三、哈夫曼编码3.1 哈夫曼编码简述3.2 特例详解 四、RGB图像压缩 一、信息熵是什么&#xff1f; 信息&#xff1a;信息&#xff0c;指音讯、消息、通讯系统传输和处理的对象&#xff0c;…

6.信息论(一):信息量、熵和最优编码

前言 信息论是由克劳德香农发展&#xff0c;用来找出信号处理与通信操作的基本限制&#xff0c;如数据压缩、可靠的存储和数据传输等。自创立以来&#xff0c;已被应用多个领域&#xff0c;例如自然语言处理(NLP)、机器学习等领域。 定长编码(Block Codes) 让我们从一个例子…

信息熵与编码

文章目录 一、信息熵的概念二、利用编码求压缩率1.香农-凡诺编码2.霍夫曼编码 三、实验证明图像字节四、文献参考 一、信息熵的概念 信息是个很抽象的概念。人们常常说信息很多&#xff0c;或者信息较少&#xff0c;但却很难说清楚信息到底有多少。比如一本五十万字的中文书到…

熵编码原理

熵编码原理 一.熵编码原理1.原理介绍2.常见方案3.整数位元法4.熵编码模型二.熵编码CABAC介绍1.二进制化2.上下文建模3.二进制算术编码常规编码区间重归一化旁路编码 一.熵编码原理 1.原理介绍 熵编码即编码过程中按熵原理不丢失任何信息的编码。信息熵为信源的平均信息量&…

熵编码:CABAC

基于上下文的二进制算术编码&#xff08;Context-Based Adaptive Binary Arithmetic Coding,CABAC&#xff09;将自适应二进制算术编码和上下文模型相结合。是H.265/HEVC的主要熵编码方案。 主要包括三个步骤&#xff1a; 二进制化&#xff1b; 上下文建模&#xff1b; 二进…

熵编码:算术编码

算术编码不是简单的将每个信源符号映射成一个码字&#xff0c;而是对整个输入序列分配一个码字&#xff0c;所以平均意义上可以为每个信源符号分配长度小于1的码字。 算术编码操作简单&#xff0c;下面以一个实例讲解算术编码的原理&#xff1a; 设信源有a,b,c,d四种符号&…

GitLab-CI基础使用总结

思路梳理 下图是GitLab-ci的实现结构图&#xff1a; (实际结构会有出入&#xff0c;画成这样只是便于理解) GitLab:是一个基于 Git 的代码托管平台&#xff0c;提供了代码仓库管理、问题跟踪、CI/CD 等功能。它可以用于团队协作开发、版本控制、代码审查等场景。GitLab-runne…

Git --- Git Gui

目录 1. 创建和删除分支(了解即可) 2. Git Gui 3. 什么是ssh key 4. git/gitee生成密钥并通过 第一步&#xff1a;本地电脑配置 第二步&#xff1a;远程gitee仓库配置 第三步&#xff1a;修改你本地的ssh remote url. 不用https协议&#xff0c;改用git 协议 第四步&#x…

git与gerrit基础概念

序 本文记录了 git 与 gerrit 学习所得 重点关注于当前所用到的实际操作部分&#xff0c;其余理论部分以及更复杂用法留待将来用到时继续补充 1 Git 与 Gerrit Git 是当前全世界流行的分布式版本控制工具&#xff0c;但是只适用于纯文本文件&#xff0c;包括markdown、网页、…

Git入门|Git的基本用法(一)

1. Git的安装 首先在安装之前确认一下系统有没有安装Git。在Terminal中输入&#xff1a; git --version若确认系统没有安装git&#xff0c;可通过以下指南安装&#xff1a; Getting Started - Installing Git 2. 创建本地Git库 每次进行新项目时&#xff0c;都需要创建一个…

Gitlab-CI入门配置

Gitlab-CI使用及.gitlab-ci.yml配置 Gitlab-CI/CD 持续集成测试篇 Gitlab-CI/CD使用场景在这里插入代码片 首先&#xff0c;公司使用Gitlab作为工作仓库进行代码发布及版本控制&#xff0c;Gitlab内置了CI/CD的工具&#xff0c;这些工具可以用于代码提交的同时完成镜像构建、…