算术编码原理及其python实现

article/2025/9/16 3:12:58

在这里插入图片描述

目录

  • 1. 原理部分:
  • 2. 香农界理论分析:
  • 3. 代码实现:
  • 4.实验结果

1. 原理部分:

原理部分参考什么是算术编码
一个从信源序列到不可压缩二元序列的一个可逆映射,假设序列 { X 1 … X n } \{X_{1} \ldots X_{n}\} {X1Xn} X n X_{n} Xn有m个取值,将这个序列进行算术编码,即利用m个取值对应的概率,对这个符号序列编码成二进制序列 { Y 1 … Y k } \{Y_{1} \ldots Y_{k}\} {Y1Yk}的过程。

2. 香农界理论分析:

算术编码比起Haffman编码更逼近于香农极限

香农第一定理/无失真信源编码定理/变长信源编码定理:
  无损情况下,数据压缩的临界值为这段信息的信息熵(编码后平均码长逼近信源信息熵),编码后的输出序列中每个新符号是接近等概的

可以通过仿真查看随着序列长度n的增大,算术编码的平均码长对信息熵的逼近过程,但n越大编解码速度也会快速提升(编解码算法复杂度没去研究):
在这里插入图片描述
平均码长-序列长度n曲线:
(应该是因为仿真用的文件不够大,导致n变大时后面出现波动)
在这里插入图片描述

3. 代码实现:

参考github

编码过程:

  1. 计算符号频数,将符号序列截断依次编码
  2. 根据符号序列中的次序和符号频数划分区间和映射区间
  3. 在最终目标区间中选取一个二进制表示最短的小数,去掉0和小数点后完成二进制编码

read_encode.py

import os
import math# 以二进制的方式读取文件,结果为字节
def fileload(filename):file_pth = os.path.dirname(__file__) + '/' + filenamefile_in = os.open(file_pth, os.O_BINARY | os.O_RDONLY)file_size = os.stat(file_in)[6]data = os.read(file_in, file_size)os.close(file_in)return data# 计算文件中不同字节的频数和累积频数
def cal_pr(data):pro_dic = {}data_set = set(data)for i in data_set:pro_dic[i] = data.count(i)  # 统计频数sym_pro = []  # 频数列表accum_pro = []  # 累积频数列表keys = []  # 字节名列表accum_p = 0data_size = len(data)for k in sorted(pro_dic, key=pro_dic.__getitem__, reverse=True):sym_pro.append(pro_dic[k])keys.append(k)for i in sym_pro:accum_pro.append(accum_p)accum_p += iaccum_pro.append(data_size)tmp = 0for k in sorted(pro_dic, key=pro_dic.__getitem__, reverse=True):pro_dic[k] = [pro_dic[k], accum_pro[tmp]]tmp += 1return pro_dic, keys, accum_pro# 小数十进制转二进制
def dec2bin(x_up, x_down, L):bins = ""while ((x_up != x_down) & (len(bins) < L)):x_up *= 2if x_up > x_down:bins += "1"x_up -= x_downelif x_up < x_down:bins += "0"else:bins += "1"return bins# 编码
def encode(data, pro_dic, data_size):C_up = 0A_up = A_down = C_down = 1for i in range(len(data)):C_up = C_up * data_size + A_up * pro_dic[data[i]][1]C_down = C_down * data_sizeA_up *= pro_dic[data[i]][0]A_down *= data_sizeL = math.ceil(len(data) * math.log2(data_size) - math.log2(A_up))  # 计算编码长度bin_C = dec2bin(C_up, C_down, L)amcode = bin_C[0:L]  # 生成编码return C_up, C_down, amcode

解码过程:

  1. 根据该小数找到符号频数对应区间
  2. 从区间映射到符号次序

decode_save.py

import os
import math
from fnmatch import fnmatch# 二分法搜索
def binarysearch(pro_list, target):low = 0high = len(pro_list) - 1if pro_list[0] <= target <= pro_list[-1]:while high >= low:middle = int((high + low) / 2)if (pro_list[middle] < target) & (pro_list[middle + 1] < target):low = middle + 1elif (pro_list[middle] > target) & (pro_list[middle - 1] > target):high = middle - 1elif (pro_list[middle] < target) & (pro_list[middle + 1] > target):return middleelif (pro_list[middle] > target) & (pro_list[middle - 1] < target):return middle - 1elif (pro_list[middle] < target) & (pro_list[middle + 1] == target):return middle + 1elif (pro_list[middle] > target) & (pro_list[middle - 1] == target):return middle - 1elif pro_list[middle] == target:return middlereturn middleelse:return False# 译码
def decode(C_up, C_down, pro_dic, keys, accum_pro, byte_num, data_size):byte_list = []for i in range(byte_num):k = binarysearch(accum_pro, C_up * data_size / C_down)  # 二分法搜索编码所在频数区间if k == len(accum_pro) - 1:k -= 1key = keys[k]byte_list.append(key)C_up = (C_up * data_size - C_down * pro_dic[key][1]) * data_sizeC_down = C_down * data_size * pro_dic[key][0]return byte_list  # 返回译码字节列表# 整数二进制转十进制
def int_bin2dec(bins):dec = 0for i in range(len(bins)):dec += int(bins[i]) * 2 ** (len(bins) - i - 1)return dec# 保存文件
def filesave(data_after, filename):file_pth = os.path.dirname(__file__) + '/' + filename# 保存译码文件if (fnmatch(filename, "*_am.*") == True):file_open = os.open(file_pth, os.O_WRONLY | os.O_CREAT | os.O_BINARY)os.write(file_open, data_after)os.close(file_open)# 保存编码文件else:byte_list = []byte_num = math.ceil(len(data_after) / 8)for i in range(byte_num):byte_list.append(int_bin2dec(data_after[8 * i:8 * (i + 1)]))file_open = os.open(file_pth, os.O_WRONLY | os.O_CREAT | os.O_BINARY)os.write(file_open, bytes(byte_list))os.close(file_open)return byte_num  # 返回字节数

测试函数(main):amcode.py

from read_encode import *
from decode_save import *
from datetime import datetime# 计算编码效率
def code_efficiency(pro_dic, data_size, bit_num):entropy = 0# 计算熵for k in pro_dic.keys():entropy += (pro_dic[k][0] / data_size) * (math.log2(data_size) - math.log2(pro_dic[k][0]))# 计算平均码长ave_length = bit_num / data_sizecode_efficiency = entropy / ave_lengthprint("The code efficiency is %.3f%%" % (code_efficiency * 100))# 主函数
def amcode():filename = ["1","2"]filetype = [".txt",".jpg"]for i in range(1):  # 预留多个文件一块处理print(60 * "-")print("Loading file:", filename[i] + filetype[i])t_begin = datetime.now()# 读取源文件data = fileload(filename[i] + filetype[i])data_size = len(data)print("Calculating probability of bytes..")# 统计字节频数pro_dic, keys, accum_pro = cal_pr(data)amcode_ls = ""C_upls = []C_downls = []byte_num = 1000  # 每次编码的字节数integra = math.ceil(data_size / byte_num)  # 迭代次数print("\nEncoding begins.")# 编码for k in range(integra):C_up, C_down, amcode = encode(data[byte_num * k: byte_num * (k + 1)], pro_dic, data_size)amcode_ls += amcodeC_upls.append(C_up)C_downls.append(C_down)# 保存编码文件,返回编码总字节数codebyte_num = filesave(amcode_ls, filename[i] + '.am')t_end = datetime.now()print("Encoding succeeded.")print("Saved encoding file: " + filename[i] + '.am')print("The compressing rate is %.3f%%" % ((data_size / codebyte_num) * 100))  # 压缩比code_efficiency(pro_dic, data_size, len(amcode_ls))  # 编码效率print("Encoding lasts %.3f seconds." % (t_end - t_begin).total_seconds())  # 编码时间print("Encoding speed: %.3f Kb/s" % (data_size / ((t_end - t_begin).total_seconds() * 1024)))  # 编码速率print()decodebyte_ls = []print("Decoding begins.")# 译码t_begin = datetime.now()for k in range(integra):if (k == integra - 1) & (data_size % byte_num != 0):decodebyte_ls += decode(C_upls[k], C_downls[k], pro_dic, keys, accum_pro, data_size % byte_num,data_size)else:decodebyte_ls += decode(C_upls[k], C_downls[k], pro_dic, keys, accum_pro, byte_num, data_size)# 保存译码文件filesave(bytes(decodebyte_ls), filename[i] + '_am' + filetype[i])t_end = datetime.now()print("Decoding succeeded.")print("Saved decoding file: " + filename[i] + '_am' + filetype[i])# 计算误码率errornum = 0for j in range(data_size):if data[j] != decodebyte_ls[j]:errornum += 1print("Error rate: %.3f%%" % (errornum / data_size * 100))  # 误码率print("Decoding lasts %.3f seconds." % (t_end - t_begin).total_seconds())  # 译码时间print("Decoding speed: %.3f Kb/s" % (codebyte_num / ((t_end - t_begin).total_seconds() * 1024)))  # 译码速率if __name__ == "__main__":amcode()

4.实验结果

在这里插入图片描述
解码生成的图片和文件可在代码文件中看到,通过过程中的文件大小也能看出编码后文件变小了:
在这里插入图片描述


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

相关文章

基本算术编码

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软件中视频显示的时间长度表述为 时&…

Premiere基础操作

一&#xff1a;设置缓存 二&#xff1a;ctrI导入素材 三&#xff1a;导入图像序列 四&#xff1a;打开吸附。 打开吸附后素材会对齐。 五&#xff1a;按~键可以全屏窗口。 六&#xff1a;向前选择轨道工具。 在时间线上点击&#xff0c;向前选中时间线上素材。向后选择轨道工具…