DPCM预测编码

基本原理

        预测编码本质上利用信源相邻符号之间的相关性即根据某一模型利用以往的样本值对新样本进行预测,然后将样本的实际值与其预测值相减得到一个误差值,最后对这一误差值进行编码。这种编码方式很适用于去除图像或视频这样的信源的空间冗余,即每一帧相邻的像素之间有着较强的相关性,相邻像素的像素值相差并不大。

DPCM

        DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示

        在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。

PSNR

        为了便于我们对实验结果进行定量分析,先简单介绍一下PSNR(Peak Signal to Noise Ratio,峰值信号噪声比)。

        计算均方误差 MSE(Mean Square Error):

 

其中bits为原图像地量化比特数。

一般来说:

PSNR ≥ 40 dB时,图像质量非常好,接近于原图像;

30 dB ≤ PSNR < 40 dB 时,图像有人眼可察的失真,但质量尚可;

20 dB ≤ PSNR < 30 dB 时,图像质量较差;

PSNR < 20 dB 时,图像质量人眼无法接受。

Huffman编码

Huffman是一种无失真信源编码算法,编码后可以得到即时的最佳码。我们直接调用现有Huffman编码程序,将原图像及量化后的预测误差图像进行压缩编码。 

代码实现

实现内容包括如下:

输入图片,根据给定的量化比特数进行量化和预测

void DpcmEncoding(unsigned char* yBuff, unsigned char* qPredErrBuff, unsigned char* reconBuff, int qBits) {
	int prediction;
	int predErr;	// 预测误差
	int invPredErr;	// 量化预测误差的逆量化值

	for (int i = 0; i < h; i++) {
		prediction = 128;	//预测每行的第一个像素集为128
		predErr = yBuff[i * w] - prediction;	// 预出错域为 [-128, 128] (8-bit)
		int temp = (predErr + 128) / pow(2, 8 - qBits);	// qBits-bit 量化
														// (predErr + 128) with the domain of [0, 256]
		qPredErrBuff[i * w] = PixelOverflow(temp, 0, pow(2, qBits) - 1);
		invPredErr = qPredErrBuff[i * w] * pow(2, 8 - qBits) - 128;	// 逆量化
		reconBuff[i * w] = PixelOverflow(invPredErr + prediction, 0, 255);	// 重建量化级别

		for (int j = 1; j < w; j++) {	// 从每行的第二个像素开始
			prediction = reconBuff[i * w + j - 1];	// 设置为预测的上一个像素值
			predErr = yBuff[i * w + j] - prediction;	// predErr with the domain of [-255, 255] (9-bit)
			int temp = (predErr + 255) / pow(2, 9 - qBits);	// qBits-bit 量化
															// (predErr + 255) with the domain of [0, 510]; [0, 2^(qBits) - 1] after division
			qPredErrBuff[i * w + j] = PixelOverflow(temp, 0, (pow(2, qBits) - 1));	// (predErr + 255) with the domain of [0, 255]
			invPredErr = qPredErrBuff[i * w + j] * pow(2, 9 - qBits) - 255;
			reconBuff[i * w + j] = PixelOverflow(invPredErr + prediction, 0, 255);	// 重建级别
		}
	}
}

同时输出预测误差图像和重建图像,计算:

        原图像的概率分布和熵;预测误差图像的概率分布和熵;重建图像的PSNR

void PrintPMF_Entropy(unsigned char* buffer, int qBits, const char* pmfFileName, const char* entrFileName) {
	int count[256] = { 0 };	// 计数器
	double freq[256] = { 0 };	// 频率
	double entropy = 0;

	/* 计算每个灰度的频率 */
	for (int i = 0; i < w * h; i++) {
		int index = (int)buffer[i];
		count[index]++;
	}

	/* 计算PMF和熵 */
	for (int i = 0; i < 256; i++) {
		freq[i] = (double)count[i] / (w * h);
		if (freq[i] != 0) {
			entropy += (-freq[i]) * log(freq[i]) / log(2);
		}
	}

	/* 将统计数据输出到csv文件中 */
	FILE* pmfFilePtr;
	FILE* entrFilePtr;
	if (fopen_s(&pmfFilePtr, pmfFileName, "wb") == 0) {
		cout << "Successfully opened \"" << pmfFileName << "\".\n";
	}
	else {
		cout << "WARNING!! Failed to open \"" << pmfFileName << "\".\n";
		exit(-1);
	}
	if (fopen_s(&entrFilePtr, entrFileName, "ab") == 0) {
		cout << "Successfully opened \"" << entrFileName << "\".\n";
	}
	else {
		cout << "WARNING!! Failed to open \"" << entrFileName << "\".\n";
		exit(-1);
	}


	fprintf(pmfFilePtr, "Symbol,Frequency\n");
	for (int i = 0; i < 256; i++) {
		fprintf(pmfFilePtr, "%-3d,%-8.2e\n", i, freq[i]);	// 将数据输出到文件中(csv文件以“,”作为分隔符)
	}
	fprintf(entrFilePtr, "%d,%.4lf\n", qBits, entropy);

	fclose(pmfFilePtr);
	fclose(entrFilePtr);
}

void PrintPSNR(unsigned char* oriBuffer, unsigned char* recBuffer, int qBits, const char* psnrFileName) {
	double mse;
	double sum = 0;
	double temp;
	double psnr;

	for (int i = 0; i < w * h; i++) {
		temp = pow((oriBuffer[i] - recBuffer[i]), 2);
		sum += temp;
	}
	mse = sum / (w * h);
	psnr = 10 * log10(255 * 255 / mse);

	/* 将统计数据输出到csv文件中 */
	FILE* outFilePtr;
	if (fopen_s(&outFilePtr, psnrFileName, "ab") == 0) {
		cout << "Successfully opened \"" << psnrFileName << "\".\n";
	}
	else {
		cout << "WARNING!! Failed to open \"" << psnrFileName << "\".\n";
		exit(-1);
	}
	fprintf(outFilePtr, "%d,%lf\n", qBits, psnr);

	fclose(outFilePtr);
}

将预测误差图像写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。

或者直接将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。

最后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质量)。压缩质量以PSNR进行计算。

量化预测误差QPE(quantisied prediction error);概率分布PMF

int main(int argc, char* argv[]) {
	int qBits = 8;
	const char* orFileName = "Lena.yuv";
	const char* qpeFileName = "Lena_QPE (8 bit).yuv";   //量化预测误差文件的名称
	const char* recFileName = "Lena_reconstruction (8 bit).yuv";    // 重建级文件的名称
	FILE* oriFilePtr;
	FILE* qpeFilePtr;
	FILE* recFilePtr;

	/* 打开文件 */
	if (fopen_s(&oriFilePtr, orFileName, "rb") == 0) {
		cout << "Successfully opened \"" << orFileName << "\".\n";
	}
	else {
		cout << "WARNING!! Failed to open \"" << orFileName << "\".\n";
		exit(-1);
	}
	if (fopen_s(&qpeFilePtr, qpeFileName, "wb") == 0) {
		cout << "Successfully opened \"" << qpeFileName << "\".\n";
	}
	else {
		cout << "WARNING!! Failed to open \"" << qpeFileName << "\".\n";
		exit(-1);
	}
	if (fopen_s(&recFilePtr, recFileName, "wb") == 0) {
		cout << "Successfully opened \"" << recFileName << "\".\n";
	}
	else {
		cout << "WARNING!! Failed to open \"" << recFileName << "\".\n";
		exit(-1);
	}

	/* 缓冲区分配 */
	unsigned char* oriYBuff = new unsigned char[w * h];
	unsigned char* qpeYBuff = new unsigned char[w * h];
	unsigned char* recYbuff = new unsigned char[w * h];
	unsigned char* uBuff = new unsigned char[w * h / 4];
	unsigned char* vBuff = new unsigned char[w * h / 4];

	/* 读取灰度数据 */
	fread(oriYBuff, sizeof(unsigned char), w * h, oriFilePtr);

	/* DPCM */
	DpcmEncoding(oriYBuff, qpeYBuff, recYbuff, qBits);
	memset(uBuff, 128, w * h / 4);
	memset(vBuff, 128, w * h / 4);
	fwrite(qpeYBuff, sizeof(unsigned char), w * h, qpeFilePtr);
	fwrite(uBuff, sizeof(unsigned char), w * h / 4, qpeFilePtr);    // Greyscale image
	fwrite(vBuff, sizeof(unsigned char), w * h / 4, qpeFilePtr);
	fwrite(recYbuff, sizeof(unsigned char), w * h, recFilePtr);
	fwrite(uBuff, sizeof(unsigned char), w * h / 4, recFilePtr);    // Greyscale image
	fwrite(vBuff, sizeof(unsigned char), w * h / 4, recFilePtr);

	/* 在csv中写入数据 */
	PrintPMF_Entropy(oriYBuff, qBits, "Lena-PMF (8 bit).csv", "Lena-entropy.csv");
	PrintPMF_Entropy(qpeYBuff, qBits, "Lena_QPE-PMF (8 bit).csv", "Lena_QPE-entropy.csv");
	PrintPSNR(oriYBuff, recYbuff, qBits, "Lena_reconstruction-PSNR.csv");

	fclose(oriFilePtr);
	fclose(qpeFilePtr);
	fclose(recFilePtr);
	delete[]oriYBuff;
	delete[]qpeYBuff;
	delete[]recYbuff;
	delete[]uBuff;
	delete[]vBuff;
}

实验结果

DPCM部分

量化比特数bitPSNR(客观评价)预测误差图像重建图像(主观评价)
851.147337
423.13889
211.93524
19.95645
  • 8bit量化时,重建效果较好;4bit量化时出现肉眼可察的失真;2bit量化以下时图像质量无法接受。
  • 在这里我们也可以发现一点,例如对于量化误差进行1 bit量化,并不代表重建图像只有0和255两个亮度电平,这也是相比于直接对原图像进行量化的优势之一。     
  • 对于PSNR的计算符合上文的分析。

         即一般来说:

        PSNR ≥ 40 dB时,图像质量非常好,接近于原图像;

        30 dB ≤ PSNR < 40 dB 时,图像有人眼可察的失真,但质量尚可;

        20 dB ≤ PSNR < 30 dB 时,图像质量较差;

        PSNR < 20 dB 时,图像质量人眼无法接受。

量化比特数bit原文件概率分布预测误差概率分布
8
4
2
1

   

量化比特数bit原文件熵DPCM后熵
87.05344.6098
41.2735
20.3984
10.1545

可以看到,原图像中存在较大的相关性,进行了DPCM编码后,相关性得到了较好的去除,从而实现了压缩。

哈夫曼编码部分

将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。最

后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率。

DPCM+8bit量化+熵编码-----压缩比: 96/46=2.1

仅进行熵编码---------压缩比:96/69=1.4

调用了已给的哈夫曼程序后,可以看到经过DPCM编码后再进行哈夫曼编码得到的文件体积明显更小,即系统的压缩效果更好。

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值