【数据压缩】第六次作业——DPCM压缩系统的实现与分析

【数据压缩】第六次作业——DPCM压缩系统的实现与分析,第1张

目录

一、 实验项目名称

二、 实验目的

三、实验内容

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. PSNR

PNSR(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.yuv589,824531,6850.9018bit589,824353,2360.599
Birds.yuv589,824531,6850.9014bit589,824255,0280.432
Birds.yuv589,824531,6850.9012bit589,824241,2170.409
Birds.yuv589,824531,6850.9011bit589,824244,7560.415
Camman.yuv98,30465,7810.6698bit98,30439,6230.403
Camman.yuv98,30465,7810.6694bit98,30421,626 0.220
Camman.yuv98,30465,7810.6692bit98,30418,1090.184
Camman.yuv98,30465,7810.6691bit98,30412,3020.125
Clown.yuv98,30474,9130.7628bit98,30447,9180.487
Fruit.yuv98,30474,4720.7588bit98,30442,0930.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,是我们希望的正确答案

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/884123.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-14
下一篇 2022-05-14

发表评论

登录后才能评论

评论列表(0条)