目录
一、 实验项目名称
二、 实验目的
三、实验内容
1.DPCM编解码原理
2.DPCM编码系统的设计
3. PSNR
四、实验步骤
1. 编写DPCM系统
2. 计算PSNR
3. 得到概率分布图和图像压缩比
五、 实验结果
六、 实验问题
一、 实验项目名称
DPCM压缩系统的实现和分析
二、 实验目的掌握DPCM编解码系统的基本原理。初步掌握实验用C/C++/Python等语言编程实现DPCM编码器,并分析其压缩效率。
三、实验内容 1.DPCM编解码原理DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。
2.DPCM编码系统的设计在本次实验中,我们采用固定预测器和均匀量化器。预测器采用左侧、上方预测均可。
量化器采用8比特均匀量化。本实验的目标是验证DPCM编码的编码效率。首先读取一个256级的灰度图像,采用自己设定的预测方法计算预测误差,并对预测误差进行8比特均匀量化。还可对预测误差进行1比特、2比特和4比特的量化设计。在DPCM编码器实现的过程中可同时输出预测误差图像和重建图像。将预测误差图像写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。最后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质量)。压缩质量以PSNR进行计算。
3. PSNRPNSR(Peak Signal to Noise Ratio),即峰值信噪比,计算公式如下:
其中,MAXI是表示图像点颜色的最大数值,如果每个像素值用 8 位表示,那么就是 255
MSE(Mean squared error),均方误差,计算公式如下:
m*n表示图像大小,I(i,j)和K(i,j)分别为两幅图像对应点的像素值。
PSNR越大表示重建图像越接近原始图像
四、实验步骤 1. 编写DPCM系统量化器:采取均匀量化,可以选择多个量化比特
预测器:采取左侧预测
代码如下:
第一部分:
这里一次性处理了所有8幅4:2:0格式的yuv图像文件,第一幅图像大小为768*512,为彩色图像,单独处理,u和v分量从原文件读取;剩余7幅图像大小为256*256,为黑白图像,u和v分量直接赋值128
int main(int argc,char * argv[])
{
FILE* ori_Y = NULL; //指向输入原始YUV文件指针
FILE* pred_Y = NULL; //指向输出预测误差yuv文件指针
FILE* rec_Y = NULL; //指向输出重建yuv文件指针
unsigned char* oriBuf = NULL; //原始y缓存区
unsigned char* uBuf = NULL; //u缓存区
unsigned char* vBuf = NULL; //v缓存区
unsigned char* predBuf = NULL; //预测误差缓存区
unsigned char* recBuf = NULL; //重建y缓存区
int w = 0;
int h = 0;
int image_size = 0;
for (int i = 1; i < argc - 16; i++)
{
if (i == 1) //因为第一个文件大小是768*512,且为彩色图像
{
w = 768;
h = 512;
image_size = w * h;
ori_Y = fopen(argv[1], "rb");
pred_Y = fopen(argv[9], "wb");
rec_Y = fopen(argv[17], "wb");
//开辟缓存区
oriBuf = (unsigned char*)malloc(image_size);
uBuf = (unsigned char*)malloc(image_size/4);
vBuf = (unsigned char*)malloc(image_size/4);
predBuf = (unsigned char*)malloc(image_size);
recBuf = (unsigned char*)malloc(image_size);
if (ori_Y == NULL) //找不到原始yuv文件
{
printf("cannot find original yuv file\n");
exit(1);
}
else
cout<<"原始文件:"<
第二部分:
代码中"bit"代表量化比特数,设置"s" 使得可以自由选择量化比特数
void DPCM(int w, int h, unsigned char* oriBuf, unsigned char* predBuf, unsigned char* recBuf, int bit)
{
double s = 512 / (1 << bit); //未量化误差为9bit数据,若8bit量化,应该除以2=512/2^8
int max = pow(2, bit) - 1;
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++) {
if (j == 0) { //采用左边预测,当是第一列数据时误差为数据-128
predBuf[i * w + j] = round((oriBuf[i * w + j] - 128) / s + 255 / s); //量化后误差抬高255 / s
recBuf[i * w + j] = (predBuf[i * w + j] - 255 / s) * s + 128; //重建值为反量化后的误差+128
if (predBuf[i * w + j] > max)
predBuf[i * w + j] = max;
if (predBuf[i * w + j] < 0)
predBuf[i * w + j] = 0;
if (recBuf[i * w + j] > 255)
predBuf[i * w + j] = 255;
if (recBuf[i * w + j] < 0)
recBuf[i * w + j] = 0;
if (bit < 8) //提亮,保持在128附近
predBuf[i * w + j] += 128;
}
else {
predBuf[i * w + j] = round((oriBuf[i * w + j] - recBuf[i * w + j - 1]) / s + 255 / s); //量化后误差255 / s
recBuf[i * w + j] = (predBuf[i * w + j] - 255 / s) * s + recBuf[i * w + j - 1]; //重建值为反量化后的误差+上一个重建值
if (predBuf[i * w + j] > max)
predBuf[i * w + j] = max;
if (predBuf[i * w + j] < 0)
predBuf[i * w + j] = 0;
if (recBuf[i * w + j] > 255)
predBuf[i * w + j] = 255;
if (recBuf[i * w + j] < 0)
recBuf[i * w + j] = 0;
if (bit < 8) //提亮,保持在128附近
predBuf[i * w + j] += 128;
}
}
}
2. 计算PSNR
void PSNR(int w, int h, unsigned char* oriBuf, unsigned char* recBuf)
{
double mse = 0;
double psnr = 0;
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++)
mse += pow((recBuf[i * w + j] - oriBuf[i * w + j]), 2);
mse = mse / (w * h);
psnr = 10 * log10(pow(255, 2) / mse);
cout << "PSNR:" << psnr << endl;
}
3. 得到概率分布图和图像压缩比
将原始文件、预测误差文件分别输入Huffman编码器进行编码,得到输出码流、给出概率分布图并计算压缩比。
概率分布图使用matlab进行绘制,代码如下:
clear;clc
load YUV.mat
x=0:1:255;
c=size(YUV,2);
mytitle={"Birds_huff","Birds_pred_huff","Birds1_pred_huff","Birds2_pred_huff","Birds4_pred_huff","Camman_huff","Camman_pred_huff","Camman1_pred_huff","Camman2_pred_huff","Camman4_pred_huff","Clown_huff","Clown_pred_huff","Fruit_huff","Fruit_pred_huff"};
for i=1:c
figure(i)
bar(x,YUV(:,i));
axis([0 255 0 0.2]);
title(mytitle(1,i));
grid on
end
其中YUV存取了概率分布值
压缩比由图片大小计算得出。
五、 实验结果原始图像,预测误差图像,重建图像,PSNR,概率分布图
原始图像 | 预测误差图像 | 重建图像 | PSNR | |
8 bit 概率分布图 | 51.1417 | |||
4 bit | 28.7952 | |||
2 bit | 13.3436 | |||
1 bit | 9.11363 | |||
8 bit | 51.1239 | |||
4 bit | 28.8787 | |||
2 bit | 10.9003 | |||
1 bit | 7.14243 | |||
8 bit | 51.1422 | |||
4 bit | 28.8409 | |||
2 bit | 12.9928 | |||
1 bit | 9.37571 | |||
8 bit | 51.1538 | |||
8 bit | 51.1338 | |||
8 bit | 51.1338 | |||
8 bit | 51.1402 | |||
8 bit | 51.1347 |
从以上结果可以看出:
1. 量化比特数越大,PSNR越高,图像越接近原图像;反之比特数越低,PSNR越低,图像质量越差,1bit量化只剩下黑白两块,已经完全看不出原图像的样子。
2. 预测误差的概率分布图接近Laplace分布,更容易进行压缩。
3. Birds图像是彩色图,处理的时候仅处理了亮度分量Y,U和V分量仅直接是原图值,所以得到的概率分布图会显示出原图的U和V的值(或许彩色图像的U和V也需要处理)
压缩比
原文件大小/字节 | 熵编码文件大小 | 压缩比 | 量化比特数 | 预测误差文件大小 | DPCM+熵编码文件大小 | 压缩比 | |
Birds.yuv | 589,824 | 531,685 | 0.901 | 8bit | 589,824 | 353,236 | 0.599 |
Birds.yuv | 589,824 | 531,685 | 0.901 | 4bit | 589,824 | 255,028 | 0.432 |
Birds.yuv | 589,824 | 531,685 | 0.901 | 2bit | 589,824 | 241,217 | 0.409 |
Birds.yuv | 589,824 | 531,685 | 0.901 | 1bit | 589,824 | 244,756 | 0.415 |
Camman.yuv | 98,304 | 65,781 | 0.669 | 8bit | 98,304 | 39,623 | 0.403 |
Camman.yuv | 98,304 | 65,781 | 0.669 | 4bit | 98,304 | 21,626 | 0.220 |
Camman.yuv | 98,304 | 65,781 | 0.669 | 2bit | 98,304 | 18,109 | 0.184 |
Camman.yuv | 98,304 | 65,781 | 0.669 | 1bit | 98,304 | 12,302 | 0.125 |
Clown.yuv | 98,304 | 74,913 | 0.762 | 8bit | 98,304 | 47,918 | 0.487 |
Fruit.yuv | 98,304 | 74,472 | 0.758 | 8bit | 98,304 | 42,093 | 0.428 |
从以上结果可以看出:
经过DPCM压缩系统处理后再进行熵编码,压缩效果明显比只进行熵编码要好
(对于灰白图像,量化比特数越小,压缩比越低,但彩色图像没有处理U和V,1bit压缩和2bit压缩比相近)
六、 实验问题最开始做2bit和1bit量化时,出现了重建图片为灰色,预测误差为黑色的情况,经过调试后,发现预测误差值全部为1,重建图像值全部为128。
仔细查看代码后发现有如下代码:
int s = 512 / (1 << bit);
predBuf[i * w + j] = (oriBuf[i * w + j] - 128) / s + 255 / s;
整数除法运算先去尾再转换成浮点,因此这里分开除以整型s实际上损失了本不应该损失的小数,类比下面例子
int a = 5;
int b = 2;
int c = a / b + a / b;
结果c=4,但实际上我们希望c结果是5
因此这里做了修改:
double s = 512 / (1 << bit);
predBuf[i * w + j] = round((oriBuf[i * w + j] - 128) / s + 255 / s)
s设置为double,做除法运算不会直接去除小数,并使用round函数做了一个四舍五入使结果更准确。类比下面例子:
int a = 5;
double b = 2;
int c = a / b + a / b;
结果c=5,是我们希望的正确答案
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)