C#,数值计算——算术编码压缩技术与方法(Compression by Arithmetic Coding)源代码

news/2024/11/16 15:04:31/

算术编码的数据压缩

算术编码是无损和有损数据压缩算法中常用的一种算法。

这是一种熵编码技术,其中常见符号比罕见符号用更少的比特进行编码。与诸如霍夫曼编码之类的众所周知的技术相比,它具有一些优势。本文将详细描述CACM87算术编码的实现,让您很好地了解实现它所需的所有细节。

从历史的角度来看,这篇文章是我20多年前写的一篇关于算术编码的数据压缩文章的更新。这篇文章发表在多布博士期刊的印刷版上,这意味着为了避免过多的页数,我们进行了大量的编辑。特别是,多布博士的文章结合了两个主题:算术编码的描述,以及使用PPM(部分匹配预测)进行压缩的讨论。

因为这篇新文章将在网上发布,空间考虑不再是一个重要因素,我希望这能让我公正地对待算术编码的细节。PPM本身就是一个有价值的话题,将在后面的文章中讨论。我希望这项新的努力,虽然冗长得令人恼火,但将是对我在1991年想做的主题的彻底解释。

我认为理解算术编码的最好方法是将其分为两部分,我将在本文中使用这个想法。首先,我将描述算术编码是如何工作的,使用标准C++数据类型实现的常规浮点算术。这允许实现一个完全可以理解但有点不切实际的实现。换句话说,它是有效的,但它只能用于编码非常短的消息。

文章的第二部分将描述一种实现,在该实现中,我们切换到对无界二进制数进行特殊类型的数学运算。这本身就是一个有点令人难以置信的话题,所以如果你已经理解了算术编码,它会有所帮助——你不必为同时学习两件事而烦恼。

最后,我将介绍用现代C++编写的工作示例代码。它不一定是世界上优化程度最高的代码,但它是可移植的,很容易添加到现有项目中。它应该非常适合学习和试验这种编码技术。

基本原理

关于算术编码,首先要了解的是它产生了什么。算术编码采用由符号(几乎总是八位字符)组成的消息(通常是一个文件),并将其转换为大于或等于零且小于1的浮点数。这个浮点数字可能很长——实际上,整个输出文件都是一个长数字——这意味着它不是传统编程语言中习惯使用的普通数据类型。我的算法实现必须从头开始,一点一点地创建这个浮点数,同样地,一点地读入并解码它。

这个编码过程是逐步完成的。当文件中的每个字符都被编码时,一些比特将被添加到编码的消息中,因此随着算法的进行,它会随着时间的推移而建立起来。

关于算术编码,需要理解的第二件事是,它依赖于一个模型来表征它正在处理的符号。该模型的工作是告诉编码器给定消息中字符的概率是多少。如果模型给出了消息中字符的准确概率,那么它们将被编码为非常接近最优。如果模型歪曲了符号的概率,那么编码器实际上可能会扩展消息,而不是压缩它!

using System;

namespace Legalsoft.Truffer
{
    /// <summary>
    /// compression by arithmetic coding
    /// </summary>
    public class Arithcode
    {
        private int NWK { get; } = 20;
        private int nch { get; set; }
        private int nrad { get; set; }
        private int ncum { get; set; }
        private int jdif { get; set; }
        private int nc { get; set; }
        private int minint { get; set; }
        private int[] ilob { get; set; }
        private int[] iupb { get; set; }
        private int[] ncumfq { get; set; }

        public Arithcode(int[] nfreq, int nnch, int nnrad)
        {
            this.nch = nnch;
            this.nrad = nnrad;
            this.ilob = new int[NWK];
            this.iupb = new int[NWK];
            this.ncumfq = new int[nch + 2];

            if (nrad > 256)
            {
                throw new Exception("output radix must be <= 256 in Arithcode");
            }

            minint = (int)(int.MaxValue / nrad);
            ncumfq[0] = 0;
            for (int j = 1; j <= nch; j++)
            {
                ncumfq[j] = ncumfq[j - 1] + Math.Max(nfreq[j - 1], 1);
            }
            ncum = ncumfq[nch + 1] = ncumfq[nch] + 1;
        }

        public void messageinit()
        {
            jdif = (int)(nrad - 1);
            for (int j = NWK - 1; j >= 0; j--)
            {
                iupb[j] = nrad - 1;
                ilob[j] = 0;
                nc = (int)j;
                if (jdif > minint)
                {
                    return;
                }
                jdif = (int)((jdif + 1) * nrad - 1);
            }
            throw new Exception("NWK too small in arcode.");
        }

        public void codeone(int ich, byte[] code, ref int lcd)
        {
            if (ich > nch)
            {
                throw new Exception("bad ich in Arithcode");
            }
            advance(ich, code, ref lcd, 1);
        }

        public int decodeone(byte[] code, ref int lcd)
        {
            int ja = (byte)code[lcd] - ilob[nc];
            for (int j = nc + 1; j < NWK; j++)
            {
                ja *= (int)nrad;
                ja += (byte)code[lcd + j - nc] - ilob[j];
            }
            int ihi = (int)(nch + 1);
            int ich = 0;
            while (ihi - ich > 1)
            {
                int m = (int)((ich + ihi) >> 1);
                if (ja >= multdiv(jdif, ncumfq[m], (int)ncum))
                {
                    ich = (int)m;
                }
                else
                {
                    ihi = m;
                }
            }
            if (ich != nch)
            {
                advance(ich, code, ref lcd, -1);
            }
            return ich;
        }

        public void advance(int ich, byte[] code, ref int lcd, int isign)
        {
            int jh = multdiv(jdif, ncumfq[ich + 1], (int)ncum);
            int jl = multdiv(jdif, ncumfq[ich], (int)ncum);
            jdif = jh - jl;
            arrsum(ilob, iupb, jh, NWK, (int)nrad, nc);
            arrsum(ilob, ilob, jl, NWK, (int)nrad, nc);
            int j = nc;
            for (; j < NWK; j++)
            {
                if (ich != nch && iupb[j] != ilob[j])
                {
                    break;
                }
                if (isign > 0)
                {
                    code[lcd] = (byte)ilob[j];
                }
                lcd++;
            }
            if (j + 1 > NWK)
            {
                return;
            }
            nc = j;
            for (j = 0; jdif < minint; j++)
            {
                jdif *= (int)nrad;
            }
            if (j > nc)
            {
                throw new Exception("NWK too small in arcode.");
            }
            if (j != 0)
            {
                for (int k = nc; k < NWK; k++)
                {
                    iupb[k - j] = iupb[k];
                    ilob[k - j] = ilob[k];
                }
            }
            nc -= j;
            for (int k = (int)(NWK - j); k < NWK; k++)
            {
                iupb[k] = ilob[k] = 0;
            }
            return;
        }

        public int multdiv(int j, int k, int m)
        {
            return (int)((ulong)j * (ulong)k / (ulong)m);
        }

        public void arrsum(int[] iin, int[] iout, int ja, int nwk, int nrad, int nc)
        {
            int karry = 0;
            for (int j = (int)(nwk - 1); j > nc; j--)
            {
                int jtmp = ja;
                ja /= nrad;
                iout[j] = iin[j] + (jtmp - ja * nrad) + karry;
                if (iout[j] >= nrad)
                {
                    iout[j] -= nrad;
                    karry = 1;
                }
                else
                {
                    karry = 0;
                }
            }
            iout[nc] = iin[nc] + ja + karry;
        }
    }
}
 


http://www.ppmy.cn/news/696581.html

相关文章

android os n9005,基于Android 10底层的LineageOS 17.1发布-老机型获Lineage16支持

昨日,作为原生项目最强大的lineage也正是发布了安卓10版本的LineageOS 17.1系统,官 网服务器也更新了支持LineageOS 17.1的手机固件列表。不仅如此,对于一些老一点的机型 目前官方也提供了lineageOS 16的支持,ROM乐园也会尽快收集相关刷机包到对应机型,方 便大家下载刷入 …

(四)Qt 动态手势识别“手掌随动”+“握拳选择”

系列文章目录 通过Qt实现手势识别控制软件操作相关系列技术方案 &#xff08;一&#xff09;Qt 将某控件、图案绘制在最前面的方法&#xff0c;通过QGraphicsScene模块实现 &#xff08;二&#xff09;Qt QGraphicsScene模块实现圆点绘制在所有窗体的最前方&#xff0c;实现圆…

SHA-256算法及示例

1. 引言 SHA-256&#xff08;安全哈希算法&#xff0c;FIPS 182-2&#xff09;是密码学哈希函数&#xff0c;其摘要长度为256位。SHA-256为keyless哈希函数&#xff0c;即为MDC&#xff08;Manipulation Detection Code&#xff09;。【MAC消息认证码有key&#xff0c;不是key…

word删除页眉下面的横线

首先点击插入>页眉&#xff0c;输入页眉后&#xff0c;选中页眉内容&#xff1a; 点击“设计”栏&#xff1a; 点击页面边框&#xff1a; 然后按如下设置即可消除页眉下面的横线

word 删除页眉线

word技能 删除页眉线 1、双击页眉 2、选中段落号 3、选择->开始菜单下 段落设置的 表格边框 选中“ 无框线”

Poi 为word 添加页眉 获取页眉

百度真是越来越难用了&#xff0c;***&#xff01; 还是推荐大家用必应吧 // POI方案为word添加页眉public static void main(String[] args) throws IOException {File is new File("C:\\upload\\20190510_141809278_Test.docx");//文件路径FileInputStream f…

奥斯汀页眉怎么设置_word页眉怎么插入及删除

软件大小:99.16MB 软件类型:商务办公 软件评级: 前往下载 word页眉是文档中每个页面的顶部区域,常用于显示文档的附加信息,可以让文档读者方便的知道文档的核心思想,下面小编就为大家带来word页眉使用教学,感兴趣的小伙伴快来看看吧。 word页眉使用教学: (一)Word 插入…

Word开发工具Aspose.Words功能演示:在C ++中以编程方式在Word文档中添加或删除页眉和页脚

Word文档中的页眉和页脚用于格式化和显示重要信息&#xff0c;例如主题&#xff0c;章节&#xff0c;页码&#xff0c;Copywrite等。以编程方式使用Word文档时&#xff0c;可能需要添加或删除页眉和页脚。为此&#xff0c;本文将教您如何使用C 在Word文档中添加和删除页眉和页脚…