算术编码 FPAQ0(一个简单的 order-0 算术文件压缩器)

Arithmetic Coding FPAQ0 (a simple order-0 arithmetic file compressor )

我试图理解 fpaq0 算术压缩器的代码,但我无法完全理解 it.Here 是代码的 link -fpaq0.cpp

我无法准确理解 ct[512]['2] 和 cxt 是如何 working.Also 我不是很清楚解码器是如何 working.Why 在编码每个字符之前 e.encode(0) 正在调用。

注意;我已经理解 link-Data Compression with Arithmetic Encoding

中介绍的算术编码器
  void update(int y) {
if (++ct[cxt][y] > 65534) {
  ct[cxt][0] >>= 1;
  ct[cxt][1] >>= 1;
}
if ((cxt+=cxt+y) >= 512)
  cxt=1;
}
   // Assume a stationary order 0 stream of 9-bit symbols
int p() const {
 return 4096*(ct[cxt][1]+1)/(ct[cxt][0]+ct[cxt][1]+2);
}
inline void Encoder::encode(int y) {

// Update the range
const U32 xmid = x1 + ((x2-x1) >> 12) * predictor.p();
assert(xmid >= x1 && xmid < x2);
if (y)
 x2=xmid;
else
x1=xmid+1;
predictor.update(y);

// Shift equal MSB's out
while (((x1^x2)&0xff000000)==0) {
putc(x2>>24, archive);
x1<<=8;
x2=(x2<<8)+255;
}
}

inline int Encoder::decode() {

// Update the range
const U32 xmid = x1 + ((x2-x1) >> 12) * predictor.p();
assert(xmid >= x1 && xmid < x2);
int y=0;
if (x<=xmid) {
 y=1;
 x2=xmid;
}
else
x1=xmid+1;
predictor.update(y);

// Shift equal MSB's out
while (((x1^x2)&0xff000000)==0) {
x1<<=8;
x2=(x2<<8)+255;
int c=getc(archive);
if (c==EOF) c=0;
x=(x<<8)+c;
}
return y;
}

fpaq0 是一个文件压缩器,它使用 order-0 位模型进行建模,并使用 12 位无进位算术编码器进行熵编码阶段。 ct[512][2] 存储每个上下文的计数器以计算符号概率。上下文(fpaq0 中的 order-0)是用带前导的部分位计算的(以简化计算)。

为了更容易解释,我们暂时跳过 EOF 符号。 Order-0 上下文计算如下,没有 EOF 符号(简化):

// Full byte encoding
int cxt = 1; // context starts with leading one
for (int i = 0; i < 8; ++i) {
  // Encoding part
  int y = ReadNextBit();
  int p = GetProbability(ctx);
  EncodeBit(y, p);

  // Model updating
  UpdateCounter(cxt, y); // Update related counter
  cxt = (cxt << 1) | y; // shift left and insert new bit
}

为了解码,使用没有 EOF 符号的上下文,如下所示(简化):

// Full byte decoding
int cxt = 1; // context starts with leading one
for (int i = 0; i < 8; ++i) {
  // Decoding part
  int p = GetProbability(ctx);
  int y = DecodeBit(p);
  WriteBit(y);

  // Model updating
  UpdateCounter(cxt, y); // Update related counter
  cxt = (cxt << 1) | y; // shift left and insert new bit
}

fpaq0 被设计为流压缩器。这意味着它不需要知道输入流的确切长度。那么,解码器应该如何知道何时停止的问题呢? EOF 符号正是为此而使用的。在对每个字节进行编码时,零位被编码为标志以指示后面还有更多数据。一个表示我们到达了流的末尾。所以,解码器知道什么时候停止。这就是为什么我们的上下文模型是 9 位(EOF 标志 + 8 位数据)的原因。

现在,最后一部分:概率计算。 fpaq0 仅使用 order-0 上下文中过去符号的计数来计算最终概率。

n0 = count of 0
n1 = count of 1
p = n1 / (n0 + n1)

有两个实施细节需要解决:计数器溢出和除以零。

通过在达到阈值时将两个计数减半来解决计数器溢出问题。因为,我们正在处理 p,这是有道理的。

通过在每个变量的公式中插入一个来解决被零除的问题。所以,

p = (n1 + 1) / ((n0 + 1) + (n1 + 1))