DPCM编解码 C++实现

DPCM编解码原理

1. DPCM

在这里插入图片描述

DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,
需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是
因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实
际内嵌了一个解码器,如编码器中虚线框中所示。
在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器
和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和
量化器的优化设计。

2. 关键代码

void DPCM(int bit,int W,int H,unsigned char* yBuffer,unsigned char* DevBuffer,unsigned char* rebuildBuff)
{
	int e = 0;
	int e1;
	unsigned char temp = 0;
	for (int i = 0; i < H; i++)
	{
		e = yBuffer[i*W] - 128;//第一列预测值为128 (-128,118)
		e1 = (e + 128) / pow(2, 8 - bit);//第一列量化
		if (e1 > pow(2, bit) - 1)
			e1 = pow(2, bit) - 1;
		if (e1 < 0)
			e1 = 0;
		DevBuffer[i*W] = e1;
		temp = DevBuffer[i*W] * pow(2, 8 - bit) - 128 + 128;//第一列反量化
		if (temp > 255)
			temp = 255;
		if (temp < 0)
			temp = 0;
		rebuildBuff[i*W] = temp;
		for (int j = 1; j < W; j++)
		{
			e = yBuffer[i*W + j] - rebuildBuff[i*W + j - 1];
			e1 = (e + 255) / pow(2, 9 - bit);//量化
			if (e1 > pow(2, bit) - 1)
				e1 = pow(2, bit) - 1;
			if (e1 < 0)
				e1 = 0;
			DevBuffer[i*W + j] = e1;
			temp = DevBuffer[i*W + j] * pow(2, 9 - bit) - 255 + rebuildBuff[i*W + j - 1];//反量化
			if (temp > 255)
				temp = 255;
			if (temp < 0)
				temp = 0;
			rebuildBuff[i*W + j] = temp;//重建值更新
		}
	}
}

PSNR原理

1. PSNR

PSNR是"Peak Signal to Noise Ratio"的缩写,即峰值信噪比,是一种评价图像的客观标准,它具有局限性,一般是用于最大值信号和背景噪音之间的一个工程项目。其公式如下:

在这里插入图片描述

其中,MSE是原图像与处理图像之间均方误差,其公式为:
在这里插入图片描述
Peak就是指8bits表示法的最大值255。MSE指MeanSquareError,I(角标n)指原始影像第n个pixel值,P(角标n)指经处理后的影像第n个pixel值。PSNR的单位为dB。所以PSNR值越大,就代表失真越少。

2. 关键代码

double PSNR(int bit1, int w, int h, int* dvalue)
{
	double psnr = 0;
	double mse = 0;
	double max = 0;	
	for (int i = 0; i < w*h; i++)
	{
		mse += pow(dvalue[i], 2);
	}
	mse = mse / (w*h);
	max =pow(2, bit1) - 1 ;
	psnr = 10 * log10(pow(max, 2) / mse);
	return psnr;
}

实验结果

1. 误差图像与重建图像

原图 seed.yuv:
在这里插入图片描述
分别查看1bit、2bit、4bit、5bit、8bit量化下的误差图像与重建图像。经实验发现4bit以下量化会带来较大的误差,图像难以还原,而5bit以上的量化误差较小,量化比特越大,误差越小。

比特数误差图像重建图像
1bit在这里插入图片描述在这里插入图片描述
2bit在这里插入图片描述在这里插入图片描述
4bit在这里插入图片描述在这里插入图片描述
5bit在这里插入图片描述在这里插入图片描述
8bit在这里插入图片描述在这里插入图片描述

2. 输出码流压缩比与概率分布

输出码流的概率分布图与压缩质量PSNR如下,可以看到量化比特数越大,PSNR峰值信噪比越大,即图像压缩质量越好。

bit数PSNR概率分布
原图在这里插入图片描述
1bit8.23在这里插入图片描述
2bit8.53在这里插入图片描述
3bit10.99
4bit14.54在这里插入图片描述
5bit25.23
6bit26.86
7bit27.77
8bit30.27在这里插入图片描述

3. Huffman编码

在DPCM编码器实现的过程中可同时输出预测误差图像和重建图像。将预测误差图像
写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。
将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。最
后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质
量)。压缩质量以PSNR进行计算。

在有Huffman.exe的文件cmd中输入huffcode -i seed.yuv -o seed.huff -c >seed.txt,得到.huff文件与.txt 文件,.huff文件大小与压缩比如下分析发现,系统1(DPCM+熵编码DPCM编码)处理后文件的压缩比小于系统2(仅进行熵编码)的文件,即系统2压缩效果更好。在进行Huffman编码后,文件量化比特数越大,压缩比越大,压缩效果越好。

源文件seed.yuv大小:733kb
输入文件量化比特文件大小/kb压缩比
源文件24833.83%
1bit12316.78%
2bit13418.28%
3bit13718.69%
4bit14119.24%
5bit15320.87%
6bit17023.19%
7bit17824.28%
8bit20728.24%

源码

//dpcm.cpp
#include <iostream>
#include "rgb2yuv.h"
#include <malloc.h>
#include <stdlib.h>
#define Width 500
#define Height 500

void DPCM(int bit,int W,int H,unsigned char* yBuffer,unsigned char* DevBuffer,unsigned char* rebuildBuff)
{
	int e = 0;
	int e1;
	unsigned char temp = 0;
	for (int i = 0; i < H; i++)
	{
		e = yBuffer[i*W] - 128;//第一列预测值为128 (-128,118)
		e1 = (e + 128) / pow(2, 8 - bit);//第一列量化
		if (e1 > pow(2, bit) - 1)
			e1 = pow(2, bit) - 1;
		if (e1 < 0)
			e1 = 0;
		DevBuffer[i*W] = e1;
		temp = DevBuffer[i*W] * pow(2, 8 - bit) - 128 + 128;//第一列反量化
		if (temp > 255)
			temp = 255;
		if (temp < 0)
			temp = 0;
		rebuildBuff[i*W] = temp;
		for (int j = 1; j < W; j++)
		{
			e = yBuffer[i*W + j] - rebuildBuff[i*W + j - 1];
			e1 = (e + 255) / pow(2, 9 - bit);//量化
			if (e1 > pow(2, bit) - 1)
				e1 = pow(2, bit) - 1;
			if (e1 < 0)
				e1 = 0;
			DevBuffer[i*W + j] = e1;
			temp = DevBuffer[i*W + j] * pow(2, 9 - bit) - 255 + rebuildBuff[i*W + j - 1];//反量化
			if (temp > 255)
				temp = 255;
			if (temp < 0)
				temp = 0;
			rebuildBuff[i*W + j] = temp;//重建值更新

		}
	}
}
double PSNR(int bit1, int w, int h, int* dvalue)
{
	double psnr = 0;
	double mse = 0;
	double max = 0;	
	for (int i = 0; i < w*h; i++)
	{
		mse += pow(dvalue[i], 2);
	}
	mse = mse / (w*h);
	max =pow(2, bit1) - 1 ;
	psnr = 10 * log10(pow(max, 2) / mse);
	return psnr;
}
//写txt文件
void FREQ(int w, int h, unsigned char* yBuff,char* txtFileNmae)
{
	FILE* txtFile = NULL;
	errno_t err;
	err=fopen_s(&txtFile, txtFileNmae, "wb");
	if (err == 0)
		printf("sucessful");
	else
		printf("The file was not opened");
	double* freq;
	freq= (double*)malloc(sizeof(double)*256);
	for (int i = 0; i <256; i++)
	{
		freq[i] = 0;
	}
	int temp;
	fprintf(txtFile, "%s\t\t%s\r\n", "sym", "freq");
	for (temp = 0; temp < 256; temp++)
	{
		for (int i = 0; i < w*h; i++)
		{
			if (temp == yBuff[i])
				freq[temp]++;
		}
		fprintf(txtFile, "%d\t\t%f\r\n", temp, freq[temp]/(w*h));
	}
	
	fclose(txtFile);
	free(freq);
}

void reFREQ(int b,int w, int h, unsigned char* yBuff, char* txtFileNmae)
{
	FILE* txtFile = NULL;
	errno_t err;
	err = fopen_s(&txtFile, txtFileNmae, "wb");
	if (err == 0)
		printf("sucessful");
	else
		printf("The file was not opened");
	double* freq;
	int m = pow(2, b);
	freq = (double*)malloc(sizeof(double) * m);
	for (int i = 0; i < m; i++)
	{
		freq[i] = 0;
	}
	int temp;
	fprintf(txtFile, "%s\t\t%s\r\n", "sym", "freq");
	for (temp = 0; temp < m; temp++)
	{
		for (int i = 0; i < w*h; i++)
		{
			if (temp == yBuff[i])
				freq[temp]++;
		}
		fprintf(txtFile, "%d\t\t%f\r\n", temp, freq[temp] / (w*h));
	}

	fclose(txtFile);
	free(freq);
}
// 映射至8bit
void Map_To_8(int N_bits, int width, int height, unsigned char* ybuff) 
{
	double N = pow(2, N_bits);
	for (int i = 0; i < width* height; i++) {
		*(ybuff + i) = (unsigned char)(*(ybuff + i) * (255.0 / N) + (255 / (2 * N) + 0.5));
	}
}
int main(int argc, char* argv[])
{
	int bit = 7;
	FILE* yuvFile = NULL;
	FILE* yuvFile1 = NULL;
	FILE* FileP = NULL;

	char* yuvFileName = NULL;//存原始图
	char* yuvFileName1 = NULL;//存重建图像
	char* FileNameP = NULL;//存量化误差
	char* yTXTName = NULL;
	char* rebuildTXTName = NULL;
	yuvFileName = argv[1];
	yuvFileName1 = argv[2];
	FileNameP = argv[3];
	yTXTName = argv[4];
	rebuildTXTName = argv[5];

	//打开文件
	errno_t err;
	if ((err = fopen_s(&yuvFile, yuvFileName, "rb")) == 0)//打开原始文件
		printf("The file\t%s\twas opened\n", yuvFileName);
	else
		printf("The file\t%s\twas not opened\n", yuvFileName);
	if ((err = fopen_s(&yuvFile1, yuvFileName1, "wb")) == 0)//打开重建文件
		printf("The file\t%s\twas opened\n", yuvFileName1);
	else
		printf("The file\t%s\twas not opened\n", yuvFileName1);
	if ((err = fopen_s(&FileP, FileNameP, "wb")) == 0)//打开误差文件
		printf("The file\t%s\twas opened\n", FileNameP);
	else
		printf("The file\t%s\twas not opened\n", FileNameP);

	//四个动态内存区指针
	unsigned char* rebuildBuff = NULL;//重建
	unsigned char* yBuffer = NULL;//原
	unsigned char* uBuffer = NULL;
	unsigned char* vBuffer = NULL;
	unsigned char* DevBuffer = NULL;//量化值索引
	int* dValue = NULL;
	//分配动态内存
	rebuildBuff = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height);
	yBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height);
	uBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height);
	vBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height);
	DevBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height);
	dValue = (int *)malloc(sizeof(int)*Width*Height);

	fread(yBuffer, 1, Width*Height, yuvFile);
	fread(uBuffer, 1, Width*Height, yuvFile);
	fread(vBuffer, 1, Width*Height, yuvFile);
	

	DPCM(bit, Width, Height, yBuffer, DevBuffer, rebuildBuff);

	for (int i = 0; i < Width*Height; i++)
	{
		dValue[i] = yBuffer[i] - rebuildBuff[i];
	}

	double p=PSNR(8, Width, Height, dValue);
	printf("PSNR:%lf\n", p);

	FREQ(Width, Height, yBuffer, yTXTName);//原始文件txt
	
	reFREQ(bit,Width, Height, DevBuffer,rebuildTXTName);//重建文件txt

	Map_To_8(bit, 500, 500, DevBuffer);

	fwrite(DevBuffer, sizeof(unsigned char), Width*Height, FileP);
	fwrite(uBuffer, sizeof(unsigned char), Width*Height, FileP);
	fwrite(vBuffer, sizeof(unsigned char), Width*Height, FileP);

	//动态内存写入yuvR
	fwrite(rebuildBuff, sizeof(unsigned char), Width*Height, yuvFile1);
	fwrite(uBuffer, sizeof(unsigned char), Width*Height, yuvFile1);
	fwrite(vBuffer, sizeof(unsigned char), Width*Height, yuvFile1);

	//释放内存,关闭文件
	free(yBuffer);
	free(uBuffer);
	free(vBuffer);
	free(rebuildBuff);
	free(DevBuffer);

	fclose(yuvFile);
	fclose(yuvFile1);
	fclose(FileP);
	system("pause");
	return 0;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值