栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

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

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

目录

一、 实验项目名称

二、 实验目的

三、实验内容

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 filen");
				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,是我们希望的正确答案

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/870254.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号