算术编码

article/2025/9/16 3:09:19

文章首发于我的个人博客

前言

这篇博客主要总结大二下课程《信息论》实验的内容。主要包含固定模式的算数编码以及自适应模式的算术编码。我将首先介绍这两种算术编码的基本思想和实现思路,然后给出具体的python代码并对代码中的一些关键点进行解释说明。

固定模式的算术编码

问题

设信源可能输出的符号是26个字母,且每个字母出现的概率为:a, b, c, d, e, f 均为0.1,其它是等概的,试编写程序可以对任意字母序列(如presentation)进行固定模式的算术编码,并进行相应的译码。

算法原理

固定模式算术编码

以上图为例说明固定模式算术编码的原理。首先,假设有A、B、C、D四个字符,这四个字符出现的概率分别为0.2、0.2、0.2、0.4,要求对字符序列“ADBCD”进行编码。开始编码时,概率空间为[0,1],当我们首先对A进行编码时,我们的概率空间将会缩小到字符A的区间,即[0,0.2],然后在缩小的区间内根据各个字符的概率重新分配概率空间。依次类推,当进行到最后一个字符时,我们将最后一个字符对应的概率空间作为编码区间,并且在编码区间内选择一个靠近区间上界的概率作为编码概率,利用公式 l i = l o g r 1 p ( s i ) {l_i = log_r}\frac{1}{p(s_i)} li=logrp(si)1
得到**码长。**之后将编码概率转换为与码长等长的二进制序列,即为编码的结果。

译码时,首先将二进制编码序列转换为浮点数,即还原出编码概率。然后顺序遍历编码时存储的字符概率空间改变序列与还原出的编码概率进行比较,选择相应字符概率空间对应的字符即可。这样就可以得到原字符序列。

代码

import math
from decimal import *def float2bin(x, n):'''x : float typen : bins numsreturn : list type'''bins = []while n > 0:x = x * 2if x >= 1.0:bins.append(1)else:bins.append(0)x -= int(x)n -= 1# print(bins)return binsdef bin2float(bins):'''bins: list typereturn : float type'''c = []f = -1for i in range(len(bins)):c.append(f)f -= 1num = Decimal(0.0)for i in range(len(bins)):num += Decimal(bins[i] * 2**c[i])# print(num)return numdef fixed_AC_encode(str, char, char_list):'''固定模式算术编码str: 要进行编码的字符串序列char:编码字符初始概率空间char_list: 编码过程中概率空间的变化序列return : code_length: 编码长度bins: 编码序列'''cha = char.copy()char_list.append(char.copy())		# 将初始概率空间添加进序列i = 0for s in str:	# 根据字符序列中字符出现顺序不断更改概率空间p = Decimal(cha.get(s)[1] - cha.get(s)[0])	# 求概率# print('概率:', p)if i == (len(str) - 1):	# 遍历到最后一个跳出# print(s)breakfront = cha.get(s)[0]for key in char:# 概率空间重新分配关键步骤cha[key] = [front + (p * char[key][0]), front + (p * char[key][1])]i += 1	# print(cha.items())char_list.append(cha.copy())	# 将更改后的概率空间添加进序列#print(cha.items())# 计算编码区间跨度,求码长interval = char_list[-1].get(str[-1])[1] - char_list[-1].get(str[-1])[0]# 选择编码概率encode_prob = interval * Decimal(0.8) + char_list[-1].get(str[-1])[0]	#print(interval * Decimal(0.9), char_list[-1].get(str[-1])[0])#print(interval * Decimal(0.9)+char_list[-1].get(str[-1])[0])print('编码概率:',encode_prob)	# 计算码长code_length = math.ceil(math.log2(Decimal(1) / interval))# print(code_length)bins = float2bin(encode_prob, code_length)return code_length, binsdef fixed_AC_decode(bins, char_list):'''固定模式算数解码bins:编码序列char_list:编码过程中概率空间的变化序列return: 源码'''decode_char_list = []		# 解码字符序列num = bin2float(bins)		# 将编码序列转换为概率# 解码for char in char_list:	# 遍历概率空间序列for key in char:if num >= char[key][0] and num <= char[key][1]:# print(key)decode_char_list.append(key)# print(len(char_list))decode_chars = "".join(decode_char_list)return decode_charsdef init():char = {}chars = "abcdefghijklmnopqrstuvwxyz"for i in range(len(chars)):if i <= 5:char[chars[i]] = [Decimal(i) * Decimal(0.1), Decimal(i + 1) * Decimal(0.1)]else:char[chars[i]] = [Decimal(0.6) + (i - 6) * (Decimal(0.4 / 20)), \Decimal(0.6) + (i - 5) * (Decimal(0.4 / 20))]return charif __name__ == '__main__':strList = input("请输入字符序列:").lower()print('字符序列长度:', len(strList))# 根据源码长度动态调整浮点位数getcontext().prec = math.ceil(len(strList) * 5)char = init()char_list = []		# 编码过程中概率空间的变化序列code_length, bins = fixed_AC_encode(strList, char, char_list)print('码长:',code_length)bins_new = [str(x) for x in bins]print('编码结果: ' + "".join(bins_new))decode_chars = fixed_AC_decode(bins, char_list)print('解码结果: ' + decode_chars)

运行结果如下

在代码中有几个需要注意的点我在此强调一下。

首先,代码第二行引入了python内置的decimal库,主要是为了解决python内置的浮点型精度丢失的问题(python内置浮点型保留小数点后17位)。因为当要编码的字符序列长度过长时,在计算各个字符的概率空间时由于浮点数精度的限制在更新过几轮之后遍不再改变,会导致编码错误,因此可以在在代码中看到使用了Decimal()将普通的浮点型转换为高精度的浮点型。需要注意的是,Decimal可以自定义浮点数的位数,基于这一点,我们可以根据输入的码长来动态的调整浮点数的位数,即在代码的122行getcontext().prec = math.ceil(len(strList) * 5),在这里我设置浮点位数为码长的五倍(也可自己通过输入一些测试序列来得到更为合适的值)。

其次,代码第4行和第22行定义了两个函数,分别为浮点数转二进制和二进制转浮点数函数。注意函数内部也需要将普通浮点型转为Decimal型。

下面附一张算法流程图,帮助理解代码

自适应模式的算术编码

问题

设信源可能输出的符号是26个字母,且每个字母出现的概率为:a, b, c, d, e, f 均为0.1,其它是等概的,试编写程序可以对任意字母序列(如presentation)进行固定模式的算术编码,并进行相应的译码。

算法原理

由上图说明自适应算数编码的算法原理。首先,假设有a、b、c三个字符,编码之前每个信源符号出现的频率相等,例如都等于1,利用字符出现频率除以总字符频率即可得到每个字符的概率。如果从输入字符序列读取一个字符,则该字符的频率加1,并且更新该字符的概率以及所有字符的概率空间。以上图为例,有a、b、c三个字符,要对字符序列“bccb”进行编码,初始信源字符频率都为1,概率都为1/3。当读入第一个字符’b’时,字符’b’的频率变为2,概率变为2/4,然后将所有字符的概率空间缩小到字符原’b’的概率空间内,并重新分分配每个字符的概率及概率区间。依次类推,当读取到最后一个字符时,直接取出该字符的概率区间作为编码区间,然后选择靠近区间上界的概率作为编码概率, 利用公式 l i = l o g r 1 p ( s i ) {l_i = log_r}\frac{1}{p(s_i)} li=logrp(si)1
得到**码长。**之后将编码概率转换为与码长等长的二进制序列,即为编码的结果。

译码时,与固定模式的算数编码思想相同。首先将二进制编码序列转换为浮点数,即还原出编码概率。然后顺序遍历编码时存储的字符概率空间改变序列与还原出的编码概率进行比较,选择相应字符概率空间对应的字符即可。这样就可以得到原字符序列。

代码

import math
from decimal import *def float2bin(x, n):'''x : float typen : bins numsreturn : list type'''bins = []while n > 0:x = x * 2if x >= 1.0:bins.append(1)else:bins.append(0)x -= int(x)n -= 1# print(bins)return binsdef bin2float(bins):'''bins: list typereturn : float type'''c = []f = -1for i in range(len(bins)):c.append(f)f -= 1num = Decimal(0.0)for i in range(len(bins)):num += Decimal(bins[i] * 2**c[i])# print(num)return numdef adaptive_AC_encode(strList, char, char_frequence, char_list):'''自适应模式算术编码strList: 要进行编码的字符串序列char: 编码字符初始概率空间char_frequence : 字符出现频率char_list : 编码过程中概率空间的变化序列return : code_length: 编码长度bins: 编码序列'''sum_length = 26		# 字符累计频率,初始为26cha = char.copy()char_list.append(char.copy())		# 将初始概率空间添加进序列i = 0for s in strList:	# 根据字符序列中字符出现顺序不断更改概率空间p = Decimal(cha.get(s)[1] - cha.get(s)[0])	# 求概率char_frequence[s] += 1		# 字符频率增加sum_length += 1if i == (len(strList) - 1):	# 遍历到最后一个跳出# print(s)breakfront = cha.get(s)[0]for key in char:# 概率空间重新分配关键步骤cha[key] = [front, front + p * Decimal(char_frequence[key] / sum_length)]front += p * Decimal(char_frequence[key] / sum_length)i += 1	char_list.append(cha.copy())	# 将更改后的概率空间添加进序列# 计算编码区间跨度,求码长interval = char_list[-1].get(strList[-1])[1] - char_list[-1].get(strList[-1])[0]# 选择编码概率encode_prob = interval * Decimal(0.9) + char_list[-1].get(strList[-1])[0]	print("编码概率:", encode_prob)	# 计算码长code_length = math.ceil(math.log2(Decimal(1) / interval))# print("码长:", code_length)bins = float2bin(encode_prob, code_length)return code_length, binsdef adaptive_AC_decode(bins, char_list):'''自适应模式算数解码bins:编码序列char_list:编码过程中概率空间的变化序列return: 源码'''num = bin2float(bins)		# 将编码序列转换为概率decode_char_list = []		# 解码字符序列# 解码for char in char_list:	# 遍历概率空间序列for key in char:if num >= char[key][0] and num <= char[key][1]:# print(key)decode_char_list.append(key)# print(len(char_list))decode_chars = "".join(decode_char_list)# print(decode_chars)return decode_charsdef init():char = {}char_frequence = {}chars = "abcdefghijklmnopqrstuvwxyz"for i in range(len(chars)):char[chars[i]] = [Decimal(i) * Decimal(1 / 26), Decimal(i + 1) * Decimal(1 / 26)]char_frequence[chars[i]] = 1return char, char_frequenceif __name__ == '__main__':strList = input("请输入字符序列:").lower()print('字符序列长度:', len(strList))# 根据源码长度动态调整浮点位数if len(strList) <=15:getcontext().prec = math.ceil(len(strList) * 5)else:getcontext().prec = math.ceil(len(strList) * 1.7)char, char_frequence = init()char_list = []		# 编码过程中概率空间的变化序列code_length, bins = adaptive_AC_encode(strList, char, char_frequence, char_list)print('码长:',code_length)bins_new = [str(x) for x in bins]print('编码结果: ' + "".join(bins_new))decode_chars = adaptive_AC_decode(bins, char_list)print('解码结果: ' + decode_chars)

运行结果如下

自适应模式的算术编码与固定模式的算术编码的代码结构大体上相同,主要是更新字符概率空间的方法不同,在该处注意即可。

下面附一张算法流程图,帮助理解代码


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

相关文章

算术编码原理及其python实现

目录 1. 原理部分&#xff1a;2. 香农界理论分析&#xff1a;3. 代码实现&#xff1a;4.实验结果 1. 原理部分&#xff1a; 原理部分参考什么是算术编码 一个从信源序列到不可压缩二元序列的一个可逆映射&#xff0c;假设序列 { X 1 … X n } \{X_{1} \ldots X_{n}\} {X1​…X…

基本算术编码

1.基本思想 算术编码&#xff0c;就是用一个数编码一串字符串。它的思想是这样的:对一个需要编码的字符串&#xff0c;给出一个初始区间[0, 1),这个区间被分成n份&#xff0c;n是这串字符串中不同字符的个数&#xff0c;每一份占区间长度的比例与相应字符出现次数占整个字符串…

算术编码的基本概念

二、算术编码的基本概念 算术编码属于熵编码的一种重要的类型&#xff0c;其作用同变长编码等熵编码方法类似&#xff0c;用于压缩输入数据中的统计冗余&#xff0c;并且使用算术编码的压缩同样是无损压缩。 在本系列第1篇中讨论了典型的变长编码方法——哈夫曼编码。包括哈夫…

Otsu大津算法公式推导及python实现

目录 前言 一、类间平方差是什么&#xff1f; 二、公式推导及实现 1.求类间平方差 2.opencv-python编程实现 2.1 引入图像并灰度化 2.2 查看灰度值的分布情况 2.3 求全局平均阈值 2.4 求最大类间方差 3.算法的验证 总结 前言 OTSU&#xff08;大津算法&#xff09;是…

OTSU算法原理

OTSU算法原理及实现&#xff1a; 最大类间方差是由日本学者大津(Nobuyuki Otsu)于1979年提出&#xff0c;是一种自适应的阈值确定方法。算法假设图像像素能够根据阈值&#xff0c;被分成背景[background]和目标[objects]两部分。然后&#xff0c;计算该最佳阈值来区分这两类像素…

大津阈值分割算法(OTSU处理图像)

1.算法原理简述 对于图像I(x,y)&#xff0c;前景(即目标)和背景的分割阈值记作T&#xff0c;属于前景的像素点数占整幅图像的比例记为ω0&#xff0c;其平均灰度μ0&#xff1b;背景像素点数占整幅图像的比例为ω1&#xff0c;其平均灰度为μ1。图像的总平均灰度记为μ&#xf…

[图像处理]14.分割算法比较 OTSU算法+自适应阈值算法+分水岭

参考文献&#xff1a; OTSU阈值分割孔洞填充海陆分离_SwordKii的博客-CSDN博客 drawContours函数_普通网友的博客-CSDN博客_drawcontours R329-opencv阈值分割算法——自适应阈值_Third Impact的博客-CSDN博客_opencv自适应阈值分割 分水岭算法的python实现及解析_进不去的…

OTSU算法 (大津算法)理解代码

OTSU算法&#xff1a;对图像进行二值化的算法 介绍 OTSU算法是一种自适应的阈值确定的方法&#xff0c;又称大津阈值分割法&#xff0c;是最小二乘法意义下的最优分割。 它是按图像的灰度特性&#xff0c;将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景…

Pr-快速上手-基本操作-教程

视频链接: Pr教程 视频设计到的知识点: 视频的剪辑bgm的管理添加字幕及弹幕的添加鬼畜视频的制作发布教程

Pr常用操作技巧

操作技巧持续更新中 1.premiere如何裁剪视频尺寸 视频尺寸怎么修改 1.将premiere左下角的视频素材直接向右拖动到编辑区中 2.鼠标左键单击视频,然后选择上方的【效果】 3.这时,页面左侧将弹出效果的菜单栏,依次选择效果→视频效果→变换→裁剪 4.鼠标左键按住【裁剪】不放,直接…

Premiere: 基本操作

1、首先进入编辑模式&#xff0c;使得视频能加效果 2、这时候能找到视频的效果添加&#xff0c;这里有视频的水平翻转&#xff0c;高斯模糊等&#xff0c;需要什么效果直接&#xff0c;把该效果抓到视频上就可以。

PR的入门基础教程

提示&#xff1a;这里只记述作者学习PR入门基础教程视频后的总结 文章目录 常用视频概念第1天学习总结第2天学习总结第3天学习总结 常用视频概念 第1天学习总结 第2天学习总结 第3天学习总结

Adobe Premiere Pro快速入门教程

简介&#xff1a; 适用于纯新手零基础&#xff0c;看完本教程即可完成常用视频编辑技巧。 采用Adobe Premiere Pro 2020版本 windows10操作系统 一、制作 照片音频字幕的视频 目标&#xff1a;把三张图片和一个音乐做成带字幕的视频。&#xff08;素材请自行准备&#xff0…

使用pr的8大技巧

许多小伙伴是通过pr这个软件进行素材剪辑的&#xff0c;当我们面对许多素材需要剪辑的时候&#xff0c;往往被这些素材弄得头昏脑涨&#xff0c;剪辑拼接的费时费力&#xff0c;最后出来的成品效果也不太好&#xff0c;下面就告诉大家一些pr使用的技巧&#xff0c;来提升我们的…

Premiere 零基础快速上手教程

关注并星标“高级农民工” 回复“视频”可获取视频剪辑软件和教程 在前几天的文章中&#xff0c;我分享了几款主流视频剪辑软件&#xff1a; 最主流的视频剪辑软件 简单来说就是&#xff0c;手机端用「剪映」这一款 app 就够&#xff0c;当你熟练到发现手机剪视频不方便&#x…

pr基础学习笔记

pr基础学习笔记&#xff08;正题&#xff09; 推荐几个小技巧 1.快速插入 2.快速移动小片段 2. 另&#xff1a;移动Ctrl是多轨移动移动CtrlAlt是单轨移动3.如何去除两个片段之间的空档&#xff1f; 4.两种选择工具的比较 附&#xff1a; pr快捷键 应用程序 选择工具…

pr基础入门

一、快速认识 PR 主界面并导入素材 修改 名称、位置&#xff0c;其他不用变&#xff0c;点击确定 进入界面如下&#xff1a; 认识、添加必要 窗口 导入素材方法 1.直接将文件拖入pr中 2.导入媒体以开始&#xff0c;部分右键创建 素材箱 进入素材箱&#xff0c;右键选择导入&a…

PR(基础剪辑)

一.剪辑步骤&#xff1a; 1.先粗剪后精剪&#xff1a; 粗剪&#xff1a; 精剪&#xff1a; 二. 常用键&#xff1a; 1. i&#xff1a;设置素材的起点 o&#xff1a;设置素材的终点 &#xff08;在预览素材时&#xff09; 2.快速浏览素材&#xff1a; l&#xff1a;按一次常速…

Pr 入门系列之十:基本图形

在 Pr 中&#xff0c;文字&#xff08;包括字幕&#xff09;以及形状等被归类为图形 Graphics。 一个图形剪辑里可包含多个文本图层、形状图层以及其它媒体文件等图形元素。 提示&#xff1a; 1、图形剪辑不会出现在项目面板中&#xff0c;除非升级为源图。 2、与 Ps 一样&…

PR(Adobe Premiere Pro)软件基础知识

一、基础参数设置 时长 时长为视频时间的长度。基本单位为秒。但是在PR软件中&#xff0c;有更为精准的时间单位计算为帧&#xff0c;也就是把1秒分为若干份&#xff0c;一份就是一帧&#xff0c;一帧也就可以理解为一张图片。所以在PR软件中视频显示的时间长度表述为 时&…