草庐IT

多媒体数据处理实验1:算术编码

Polaris_T 2023-07-12 原文

1. 算法描述

功能:

  给定概率字典以及待编码字符串,求出该字符串算术编码的结果(最短二进制串),并能根据算数编码结果进行解码,得到原字符串。

2.算法流程:

算术编码流程:

  (1) 首先,初始化概率区间上下界分别为1和0。读入第一个字符,并根据该字符的概率区间更新当前区间,具体方法为:
u p p e r _ b o u n d = l o w e r _ b o u n d + i n t e r v a l L e n g t h ∗ p r o b D i c t [ c h r ] [ 1 ] l o w e r _ b o u n d = l o w e r _ b o u n d + i n t e r v a l L e n g t h ∗ p r o b D i c t [ c h r ] [ 0 ] upper\_bound = lower\_bound + intervalLength * probDict[chr][1]\\ lower\_bound = lower\_bound + intervalLength * probDict[chr][0] upper_bound=lower_bound+intervalLengthprobDict[chr][1]lower_bound=lower_bound+intervalLengthprobDict[chr][0]
其中 i n t e r v a l L e n g t h intervalLength intervalLength表示当前区间长度, p r o b D i c t [ c h r ] [ 1 ] , p r o b D i c t [ c h r ] [ 0 ] probDict[chr][1], probDict[chr][0] probDict[chr][1],probDict[chr][0]分别表示当前字符的概率区间上下界。

  (2) 然后,继续读入下一字符并不断更新当前概率区间,直到最后一个字符处理完毕后退出循环,得到最终的概率区间。

  (3) 根据最终概率区间求出该区间内的最短二进制串,方法如下:从第一个二进制位开始,若当前位置1得到的数大于区间上界,则将该位置0;若当前位置1得到的数恰好在区间内(即恰好大于下界),则该位置1不变,然后直接出循环,无需生成后续的位数;若当前位置1得到的数小于下界,则该位置1。如此可保证求出的二进制串必为该区间内的最短二进制串。

算术解码流程:

  (1) 将编码得到的二进制串转为十进制小数,记作 e n c o d e d D e c encodedDec encodedDec

  (2) 先看 e n c o d e d D e c encodedDec encodedDec落在哪个字符的概率区间内,若它落在字符 c c c的概率区间内,则解码出的第一个字符即为 c c c

  (3) 接下来,继续对当前区间进行划分,得到概率字典中各字符的新概率区间,再看现在 e n c o d e d D e c encodedDec encodedDec落在哪个字符对应的区间内,则第二个解码出的字符就是该字符。

  (4) 重复步骤(3)直到循环次数达到字符串长度,完成解码过程。

求区间内的最短二进制串流程:

  从小数点后第一位开始往后:若该位置1得到的数大于区间上界,则将该位置0;若当前位置1得到的数恰好在区间内(即恰好大于下界),则该位置1不变,然后直接出循环,无需生成后续的位数;若当前位置1得到的数小于下界,则该位置1。

3. 核心代码

# 算术编码函数:输入为字符串和概率字典,返回最终区间的上下界
def arithEncode(string, probDict):
    lower_bound = Decimal('0.0')
    upper_bound = Decimal('1.0')
    for chr in string:
        intervalLength = upper_bound - lower_bound  # 区间长度
        # 不断更新区间上下界,注意必须先更新上界,否则会导致上界更新错误(因为上界的计算用的是上一次的下界)
        upper_bound = lower_bound + intervalLength * probDict[chr][1]
        lower_bound = lower_bound + intervalLength * probDict[chr][0]
        print(lower_bound, upper_bound)
    # 返回最终区间的上下界
    return lower_bound, upper_bound
 
# 求出最终区间内的最短二进制串
def dec2Bin(lower_bound, upper_bound):
    binStr = ''
    # 初始化01串转为的二进制数为0.0
    temp = Decimal(0.0)  
    i = 1  # 初始化幂为1
    while(1):
        bit = 1
        # 若当前位 置1 得到的数大于区间上界,则将该位 置0,temp不变
        if((temp + Decimal(1 / (2 ** i) * bit)) > upper_bound):
            bit = 0
            binStr += '0'
        # 若当前位 置1 得到的数恰好在区间内(即恰好大于下界),则该位 置1 不变,然后直接出循环,无需生成后续的位数
        else:
            if((temp + Decimal(1 / (2 ** i) * bit)) > lower_bound):
                binStr += '1'
                break
            # 若当前位 置1 得到的数小于下界,则该位 置1 
            else:
                binStr += '1'
        # 更新temp
        temp += Decimal(1 / (2 ** i) * bit)
        # 阶数自增1,下一轮循环时用
        i += 1
    return binStr

# 二进制串转十进制小数(乘二取整)
def bin2Dec(bin):
    dec = Decimal(0.0)
    for i in range(len(bin)):
        if(bin[i] == '1'):
            dec += Decimal(1 / (2 ** (i + 1)))
    # 这里返回的数据类型一定要是Decimal
    return dec

# 算术解码函数:输入为已编码二进制串、概率字典、字符串长度,返回解码后的字母串
def arithDecode(encodedBin, probDict, strLength):
    decodedStr = ''
    # probDict_copy存放原始的概率字典
    probDict_copy = deepcopy(probDict)
    # 二进制串转十进制小数
    encodedDec = bin2Dec(encodedBin)
    # 因为要解码出strLength个字符,所以共循环strLength次
    for _ in range(strLength):
        # 如果编码数字落在某个字符的概率区间内,则在结果中加入该字符
        for chr, interval in probDict.items():
            if((encodedDec >= interval[0]) & (encodedDec < interval[1])):
                decodedStr += chr
                # 继续对当前区间划分,看编码数字落在哪个字符对应的区间内
                # 更新字典中各字符的概率区间
                temp_lower_bound = interval[0]    # 记录当前区间下界和区间长度
                intervalLength = interval[1] - interval[0]
                for chr in probDict.keys():
                    probDict[chr][0] = temp_lower_bound + intervalLength * probDict_copy[chr][0]
                    probDict[chr][1] = temp_lower_bound + intervalLength * probDict_copy[chr][1]
                break
    return decodedStr

4. 实验结果

  结果分析:用户输入待编码各字符、各字符的概率以及需要编码的字符串,程序会先构造出概率字典,然后输出算数编码的最终结果,即最短二进制串和对应的十进制浮点数。在编码成功后,程序对编码得到的二进制串解码,最终的解码结果与原字符串一致,说明解码准确率为100%。从程序运行的结果可以看到,本实验成功实现了对给定概率字典的字符串的无损算术编码及解码。

5. 分析与总结

 1. 通过本次实验,我掌握了算术编码及解码的基本算法思想,并通过编程实现了算数编码及解码的过程,提高了自己的动手实践能力,对于Python语言中的float、Decimal等数据类型的概念和用途有了更深的理解。

  2. 题目要求我们求出在最终概率区间内的最短二进制串作为算术编码结果,但我一开始没有想到最短二进制串的求法,于是我在草稿纸上从小数位第一位开始逐位向后推算,看每一位上1和上0后得到的十进制数在不在概率区间内,最后推出了求最短二进制串的算法。其实我们在碰到看起来不会解决的问题时,不妨先静下心来拿出纸笔演算几步,说不定就能得出解法,而不是对着电脑在大脑里思考,这样做效率是很低下的。

  3. 在写算术编码函数的时候,按照算法流程,我们需要反复更新概率区间的上下界,在这个过程中我们是利用上一次的概率区间下界以及当前字符的概率区间上下界去更新的。我一开始在写这个函数的时候,没注意到必须要先更新upper_bound再更新lower_bound,而是先更新了lower_bound。这么做会导致新的区间上界是用新的区间下界更新的,而不是用上一次的下界更新的,后来我调试的时候发现了这一错误并且改正了。

  4. 关于数据类型的收获:Python中的float类型无法在任意指定的小数位上保持精确,因此在实验过程中所有的数值均转化为了Decimal类型,以保证精确计算(Decimal的精度默认是28位)。在对于运行效率要求不是很高而对于精度要求很高时,可以使用Decimal保精度,若对于运行效率的要求较高而不怎么在意精度,则使用float类型也无妨。

有关多媒体数据处理实验1:算术编码的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  4. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  5. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  8. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  9. 怎样用一台手机做自媒体? - 2

    其实做自媒体的成本并不高,入门只需要一部手机即可!在手机上找视频素材、使用手机剪辑视频、最后使用手机发布视频作品获得收益!方法并不难,今天这期内容就来给粉丝们分享一种小方法,每天稳定收益100-300,抓紧点赞收藏!1、找素材(1)使用手机拍摄自己喜欢的经典段落,使用程序把文案内容提取出来(2)也可以在豆瓣、知乎、微博等网站中找一些自己需要的文案素材(3)把文案进行润色修改,可以加入一些自己的观点(4)视频素材可以使用软件中自带的素材,也可以在素材网站中下载完整版的素材2、文案配音(1)把复制好的文案直接导入小程序中(2)调整音色、音调后一键合成音频即可(3)可以选择自己朗读配音,需要花一点时

  10. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

随机推荐