实验目的
掌握DPCM编解码系统的基本原理。初步掌握实验用C/C++/Python等语言编程实现DPCM编码器,并分析其压缩效率。
方法解释
DPCM编解码
原理
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。
在本次实验中,我们采用固定预测器和均匀量化器。
预测器
本实验采用左侧预测,即误差e= 右侧像素值 - 通过左侧像素值计算得到的预测值
量化器
由于像素值区间为[0,255],所以误差e的区间为[-255,255],约有512种值,超过存储bit数所能表示的范围。
8bit量化
对误差e除于2,e/2区间为[-127.5,127.5],再加128,得到e/2+128区间为[0.5,255.5],对此取整,量化误差区间变为[0,255],成功实现量化。
解码器
反量化+反预测
实现两种逆运算
代码实现
编码器内嵌解码器
if (y_buffer != NULL) {
for (i = 0; i < frameHeight; i++)
{
/**
每一行,以第一个值为下一个值得预测值x_pre
因为是8bit量化,所以第一个像素原值不进行量化操作
*/
*(encoded_buffer + i * frameWidth + j) = *(y_buffer + i * frameWidth + j);
x_pre = *(encoded_buffer + i * frameWidth + j);
//解码重建,第一个像素值等效为原值
*(rebuilt_buffer + i * frameWidth + j) = x_pre;
//每行第二个像素开始进行预测编码(解码重建)
for (j = 1; j < frameWidth; j++)
{ //左侧误差e
e = (int)*(y_buffer + i * frameWidth + j) - (int)x_pre;
//8bit量化预测误差
eQ = (unsigned char)(e / 2 + 128);
//输出量化预测误差
*(encoded_buffer + i * frameWidth + j) = eQ;
//反量化+反预测实现图像解码重建
x_pre = unsigned char((int)x_pre + ((int)eQ-128) * 2);
//输出解码重建像素
*(rebuilt_buffer + i * frameWidth + j) = x_pre;
}
//列计数器置零
j = 0;
}
}
实验结果
调试DPCM编解码程序
原图片
编码后图像
解码重建后图像
结果分析
huffman编码
分别对量化预测误差图和原图进行huffman编码
PSNR视频质量
评价重建图像:PSNR
PSNR,即Peak Signal to Noise Ratio,峰值信噪比,是一种评价图像的客观标准。其数学公式为:
P
S
N
R
=
10
∗
l
g
(
M
A
X
2
M
S
E
)
.
PSNR = 10*lg(\cfrac{MAX^{2}}{MSE}).
PSNR=10∗lg(MSEMAX2).
其中:MAX指最大值,在8bit灰度图中即等于255.
MSE指均方误差Mean Square Error
M
S
E
=
∑
i
=
0
M
∑
j
=
0
N
(
f
(
i
,
j
)
−
f
′
(
i
,
j
)
)
2
M
N
MSE= \cfrac{\displaystyle\sum_{i=0}^M \displaystyle\sum_{j=0}^N{(f(i,j)−f' (i,j))^{2}}}{MN}
MSE=MNi=0∑Mj=0∑N(f(i,j)−f′(i,j))2
具体到本实验中,f(x,y)和f’(x,y)即原图像和重建图像,MN即为图像的宽和高。
代码如下,
int max = 255;
double mse = 0;
for (int i = 0; i < frameHeight; i++)
for (int j = 0; j < frameWidth; j++)
mse += (y_buffer[i * frameWidth + j] - rebuilt_buffer[i * frameWidth + j]) * (y_buffer[i * frameWidth + j] - rebuilt_buffer[i * frameWidth + j]);
mse = (double)mse / (double)(frameWidth * frameHeight);
double psnr = 10 * log10((double)(max * max) / mse);
cout << "PSNR = " << psnr << endl;
结果对比
文件 | 文件大小 | 压缩比 | 图像质量 |
---|---|---|---|
原文件 | 98,304 | 100% | |
熵编码 | 69,885 | 71.1% | |
DPCM+熵编码 | 46,163 | 47.0% | 51.1772 |
完整代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int main() {
FILE* fp;
fopen_s(&fp, "Lena256B.yuv", "r");
unsigned char* y_buffer;
unsigned char* uv_buffer;
int frameWidth = 256;
int frameHeight = 256;
y_buffer = (unsigned char*)malloc(frameHeight * frameWidth);
uv_buffer = (unsigned char*)malloc(frameHeight * frameWidth / 2);
fread(y_buffer, 1, frameHeight * frameWidth, fp);
fread(uv_buffer, 1, frameHeight * frameWidth / 2, fp);
unsigned char* encoded_buffer;
unsigned char* rebuilt_buffer;
int e;
unsigned char eQ, x_pre;
int i, j = 0;
encoded_buffer = (unsigned char*)malloc(frameHeight * frameWidth);
rebuilt_buffer = (unsigned char*)malloc(frameHeight * frameWidth);
if (y_buffer != NULL) {
for (i = 0; i < frameHeight; i++)
{
*(encoded_buffer + i * frameWidth + j) = *(y_buffer + i * frameWidth + j);
x_pre = *(encoded_buffer + i * frameWidth + j);
*(rebuilt_buffer + i * frameWidth + j) = x_pre;
for (j = 1; j < frameWidth; j++)
{
e = (int)*(y_buffer + i * frameWidth + j) - (int)x_pre;
eQ = (unsigned char)(e / 2 + 128);
*(encoded_buffer + i * frameWidth + j) = eQ;
x_pre = unsigned char((int)x_pre + ((int)eQ-128) * 2);
*(rebuilt_buffer + i * frameWidth + j) = x_pre;
}
j = 0;
}
}
int max = 255;
double mse = 0;
for (int i = 0; i < frameHeight; i++)
for (int j = 0; j < frameWidth; j++)
mse += (y_buffer[i * frameWidth + j] - rebuilt_buffer[i * frameWidth + j]) * (y_buffer[i * frameWidth + j] - rebuilt_buffer[i * frameWidth + j]);
mse = (double)mse / (double)(frameWidth * frameHeight);
double psnr = 10 * log10((double)(max * max) / mse);
cout << "PSNR = " << psnr << endl;
FILE* fp_en;
fopen_s(&fp_en, "Lena256B_encoded.yuv", "wb");
fwrite(encoded_buffer, 1, frameHeight * frameWidth, fp_en);
fwrite(uv_buffer, 1, frameHeight * frameWidth / 2, fp_en);
FILE* fp_de;
fopen_s(&fp_de, "Lena256B_rebuilt.yuv", "wb");
fwrite(rebuilt_buffer, 1, frameHeight * frameWidth, fp_de);
fwrite(uv_buffer, 1, frameHeight * frameWidth / 2, fp_de);
fclose(fp);
fclose(fp_en);
fclose(fp_de);
}