十六算术编码_2算术编码举例实现
Posted 叮咚咕噜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十六算术编码_2算术编码举例实现相关的知识,希望对你有一定的参考价值。
基本原理
在一次算术编码的执行前,为简便起见,首先假设输入的信源为0/1的二进制信源,0和1的概率比为7:3。即二者的概率为:
p(0) = 0.7;
p(1) = 0.3;
假设输入的待编码信息为[0, 0, 1],在编码每一个符号时,都需要对概率区间进行分割,并通过与编码区间进行比较,判断是否输出码流的bit位,以及更新编码下一个符号的上下文。
在第一次进行分割之后,概率区间和编码区间的关系如下图所示:
第一个字符的概率区间分割之后,不满足输出码流的条件,因此结束这个字符的编码,准备开始编码下一个字符。
第二个字符依然为0,此时概率区间和编码区间的关系为:
此时概率区间已经完全处于编码区间的下半区,因此应输出一个bit-0。而后,编码区间的下半区间扩展2倍到原有的完整编码区间继续进行下一个编码。(因为每次我们比较的还是0-5000,但是我们下一次比对的是前半段区间,所以需要扩展)该过程由下图所示:
当前的编码区间是0-9800,我们设定的最后一个待编码符号为1,因此最后一次分割概率区间,选取上30%作为结果。此时的概率区间分割结果如下图所示:
由于当前的编码区间变为6860-9800,归一化之后超出了0-10000,所以需要在归一化之前需要先减去50000
代码实现
//<2>程序在概率模型为0.7、0.3最终输出0110
int main()
{
unsigned int input_data[] = { 0, 0, 1};
unsigned int input_length = sizeof(input_data) / sizeof(unsigned int);
for (int idx = 0; idx < input_length; idx++)
{
//<1>编码每一个元素
Encode_one_symbol(input_data[idx]);
}
//<1>收尾操作
Encode_stop();
return 0;
}
/*
|0-------------------7000|-------10000|
如果我们输入的要编码的数据是0,则概率区间是0-7000这部分,如果是1则是7000-10000
*/
static unsigned int probability_model[3] = { 0, 7000, 10000 }; //<2>概率模型,0出现的概率是7,1出现的概率是3
static unsigned int upper_bound = 10000; //<2>概率上限
//<2>编码器的实例
ARITHMETIC_ENCODER encoder = { 0, FULL_VALUE, 0 };
void output_bits(int bit)
{
printf("%d", bit);
while (encoder.bits_to_follow > 0) //<2>bits_to_follow在输出的时候使用,有几个待输出的bit
{
printf("%d", !bit);
encoder.bits_to_follow--;
}
}
//<2>实现元素编码:循环编码每一个传入的值,通过这个值不断的分割编码区间按照7:3的比例,最终在满足几种条件时输出
int Encode_one_symbol(unsigned int symbol)
{
long range = encoder.high - encoder.low + 1; //<2>编码区间长度,传入数据之前的长度
//<2>然后每编码一个这样的符号,需要将这个区间按照概率模型对区间进行再次分割
encoder.high = encoder.low + (range * probability_model[symbol + 1] / upper_bound) - 1;
encoder.low = encoder.low + (range * probability_model[symbol] / upper_bound);
while (1)
{
//<2>整个区间在左侧,应当输出一个0
if (encoder.high < HALF_VALUE)
{
// Output 0;
output_bits(0);
}//<2>整个区间再右侧输出一个1
else if (encoder.low >= HALF_VALUE)
{
// Output 1;
output_bits(1);
encoder.high -= HALF_VALUE;//<2>将这个在右侧的区间减半是为了能够在归一化*2之后仍在0-FULL_VALUE之间
encoder.low -= HALF_VALUE;
}//<2>大于1/4小于3/4,即小于整体的一半,应该输出,由于跨过了中点不知道是输出0还是1
else if (encoder.low >= FIRST_QUATER && encoder.high < THIRD_QUATER)
{
encoder.bits_to_follow++; //<2>无法判断输出的是0还是1
encoder.high -= FIRST_QUATER;
encoder.low -= FIRST_QUATER;
}
else //<2>进行一次区间长度更新之后,low和high依然大于整体区间的一半,不进行任何输出,直接break
{
break;
}
//<2>为了实现归一化,将这个放大一倍
encoder.low = 2 * encoder.low;
encoder.high = 2 * encoder.high + 1;
}
return 0;
}
//<2>收尾
int Encode_stop()
{
encoder.bits_to_follow++; //<2>待编码增加1
if (encoder.low < FIRST_QUATER) //<2>协议要求
{
output_bits(0);
}
else
{
output_bits(1);
}
printf("\\n");
return 0;
}
例二
输入:011
输出:10101
该例子中点阐述的是编码第二个1时,4900~7000,跨过了中点,这种情况4900-2500 ~ 7000-2500,其实是将2500-7500归一化为0-10000
以上是关于十六算术编码_2算术编码举例实现的主要内容,如果未能解决你的问题,请参考以下文章