数据压缩(九)——DPCM压缩系统的实现与分析

实验内容:将预测误差图像写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。最后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质量)。压缩质量以PSNR进行计算。

文章目录

  • (一)DPCM编解码原理
    • 关键代码
      • DPCM()函数
      • DPCM_Pixel()函数
  • (二)PSNR原理
    • 关键代码
      • PSNR()函数
      • MSE()函数
  • (三)Huffman编码原理
    • 对Huffman编码的结果分析
  • (四)完整实验过程
    • 4.1 DPCM编码 && PSNR
      • 完整代码
        • main.cpp
        • DPCM_Code.h
        • DPCM_Code.cpp
      • PSNR输出结果表
      • 信源符号概率分布图
    • 4.2 Huffman编码
      • 压缩比输出结果表
  • (五)结果汇总


(一)DPCM编解码原理

数据压缩(九)——DPCM压缩系统的实现与分析_第1张图片
  其中, d n d_n dn是预测误差, Q Q Q是量化器, P P P中存储的是重建值。

关键代码

DPCM()函数

void DPCM(unsigned char* YOrigi, unsigned char* YError, unsigned char* YRestr, int height, int width, int bits)
{
	for (int i = 0; i < height; i++)
		for (int j = 0; j < width; j++)
			DPCM_Pixel(YOrigi, YError, YRestr, i * width, j, bits);//for each pixel,use the function DPCM_Pixel() to calculate the YRestr[i] and YError[i] 
}

DPCM_Pixel()函数

void DPCM_Pixel(unsigned char* YOrigi, unsigned char* YError, unsigned char* YRestr, int pre, int j, int bits)
{
	/*for the first Pixel,suppose YError[0]=128-YOrigi[0],
	then quantify it and store*/
	int E_temp, R_temp;
	switch (j)
	{
	case 0:
		E_temp = 128 - YOrigi[pre + j];
		E_temp = ErrorQuantity(E_temp, bits);//quantity
		YError[pre + j] = E_temp;
		E_temp = invErrorQuantity(E_temp, bits);//inverse quantization
		R_temp = 128 - E_temp;
		R_temp = OverflowX(R_temp, 255, 0);//prevent overflow
		YRestr[pre + j] = (unsigned char)R_temp;
		break;
	default:
		E_temp = YOrigi[pre + j] - YRestr[pre + j - 1];
		E_temp = ErrorQuantity(E_temp, bits);//quantity
		YError[pre + j] = E_temp;
		E_temp = invErrorQuantity(E_temp, bits);//inverse quantization
		R_temp = E_temp + YRestr[pre + j - 1];
		R_temp = OverflowX(R_temp, 255, 0);//prevent overflow
		YRestr[pre + j] = (unsigned char)R_temp;
		break;
	}
}

(二)PSNR原理

   P S N R PSNR PSNR(Peak signal-to-noise ratio,常缩写为 P S N R PSNR PSNR)是一个表示信号最大可能功率的比值的工程术语。由于许多信号都有非常宽的动态范围,峰值信噪比常用对数单位(db)来表示。
  计算 P S N R PSNR PSNR要先知道 M S E MSE MSE的计算。 M S E MSE MSE是均方误差。若有两个 m × n m\times n m×n单色图像 I I I K K K,他们的均方误差定义为: M S E = 1 m n Σ i = 0 m − 1 Σ j = 0 n − 1 [ I ( i , j ) − K ( i . j ) ] 2 MSE=\frac{1}{mn} \Sigma^{m-1}_{i=0} \Sigma^{n-1}_{j=0}[I(i,j)-K(i.j)]^2 MSE=mn1Σi=0m1Σj=0n1[I(i,j)K(i.j)]2
   P S N R PSNR PSNR定义为:
P S N R = 10 × l o g 10 ( M A X I 2 M S E ) PSNR=10\times log_{10}(\frac{MAX_I^2}{MSE}) PSNR=10×log10(MSEMAXI2)
  其中, M A X I MAX_I MAXI表示图像点颜色的最大数值。

一般来说
PSNR>40dB:图像质量非常好(非常接近原始图像)
30 20 PSNR<20:图像不可接受

关键代码

PSNR()函数

double PSNR(unsigned char* YOrigi, unsigned char* YRestr, int height, int width)
{
	int fmax = pow(2, 8) - 1;
	int a = fmax * fmax;
	double mean_se = MSE(YOrigi, YRestr, height, width);
	double peak_SNR = 10 * log10((double)a / mean_se);
	return peak_SNR;
}

MSE()函数

double MSE(unsigned char* YOrigi, unsigned char* YRestr, int height, int width)
{
	int size = height * width;
	long long int sum = 0;
	double mean;
	for (int i = 0; i < size; i++)
	{
		long long int temp = (long long int)(YOrigi[i] - YRestr[i]) * (long long int)(YOrigi[i] - YRestr[i]);
		sum += temp;
	}
	mean = (double)sum / (double)size;
	return mean;
}

(三)Huffman编码原理

   H u f f m a n Huffman Huffman编码在很多地方都有提到,在此不再赘述。对此进行一个总结: H u f f m a n Huffman Huffman编码是一种无失真的编码方法,不引入任何失真。并且所编出的码字为即时码。 H u f f m a n Huffman Huffman编码非常的实用。

对Huffman编码的结果分析

  老师给了一个.exe文件,用来存放 H u f f m a n Huffman Huffman编码的结果。

  其中第一列是信源符号 a a a,第二列是信源符号出现的次数 b b b,第三列是编码的码字 c c c
  对原文件和DPCM编码后输出的error文件都进行此操作,即可判断DPCM+熵编码只进行熵编码两种方案的区别,压缩比直接由.huff文件大小比上原.yuv文件大小计算得。

(四)完整实验过程

4.1 DPCM编码 && PSNR

完整代码

  解决方案资源管理器如下:
数据压缩(九)——DPCM压缩系统的实现与分析_第2张图片

main.cpp

#include 
#include 
#include 
#include "DPCM_code.h"

using namespace std;

int main(int argc, char** argv)
{
	//moon图片464 538,使用的是4:4:4的yuv文件
	int width = atoi(argv[1]);
	int height = atoi(argv[2]);
	int bits = atoi(argv[3]);
	int Ysize = height * width;
	int Esize = height * width * 2;

	ifstream OrigiFile(argv[4], ios::binary);
	ofstream ErrorFile(argv[5], ios::binary);
	ofstream RestrFile(argv[6], ios::binary);
	if (!OrigiFile) { cout << "error to open OrigiFile!" << endl; }
	if (!ErrorFile) { cout << "error to open ErrorFile!" << endl; }
	if (!RestrFile) { cout << "error to open RestrFile!" << endl; }

	unsigned char* YOrigi = new unsigned char[Ysize];
	unsigned char* YError = new unsigned char[Ysize];
	unsigned char* YRestr = new unsigned char[Ysize];
	unsigned char* E_File = new unsigned char[Esize];

	//读入Y分量和其余分量
	OrigiFile.read((char*)YOrigi, Ysize);
	OrigiFile.read((char*)E_File, Esize);

	//进行预测和量化
	DPCM(YOrigi, YError, YRestr, height, width, bits);
	
	//对重建图像进行重置,用以判断解码端工作效果
	for (int i = 0; i < Ysize; i++)
			YRestr[i] = 0;
	
	//从YError解出量化后的yuv文件
	RDPCM(YError, YRestr, height, width, bits);
	double peak_SNR = PSNR(YOrigi, YRestr, height, width);
	cout << peak_SNR << endl;
	
	ErrorFile.write((char*)YError, Ysize);
	ErrorFile.write((char*)E_File, Esize);
	RestrFile.write((char*)YRestr, Ysize);
	RestrFile.write((char*)E_File, Esize);

	OrigiFile.close();
	ErrorFile.close();
	RestrFile.close();
	delete[]YOrigi;
	delete[]YError;
	delete[]YRestr;
	delete[]E_File;
	return 0;
}

DPCM_Code.h

#pragma once
void DPCM(unsigned char* YOrigi, unsigned char* YError, unsigned char* YRestr, int height, int width, int bits);
void DPCM_Pixel(unsigned char* YOrigi, unsigned char* YError, unsigned char* YRestr, int pre, int j, int bits);
void RDPCM(unsigned char* YError, unsigned char* YRestr, int height, int width, int bits);
void RDPCM_Pixel(unsigned char* YError, unsigned char* YRestr, int pre, int j, int bits);
double PSNR(unsigned char* YOrigi, unsigned char* YRestr, int height, int width);

DPCM_Code.cpp

#include 
#include 
#include 
#include 
#include "DPCM_code.h"

using namespace std;

//double sign(double x)
//{
//	if (x < 0)
//		return -1;
//	else if (x > 0)
//		return 1;
//	else
//		return 0;
//}

int OverflowX(int x, int High, int Low)
{
	if (x > High)
		return High;
	else if (x < Low)
		return Low;
	else
		return x;
}

double MSE(unsigned char* YOrigi, unsigned char* YRestr, int height, int width)
{
	int size = height * width;
	long long int sum = 0;
	double mean;
	for (int i = 0; i < size; i++)
	{
		long long int temp = (long long int)(YOrigi[i] - YRestr[i]) * (long long int)(YOrigi[i] - YRestr[i]);
		sum += temp;
	}
	mean = (double)sum / (double)size;
	return mean;
}

int ErrorQuantity(int X, int bits)
{
	X = X + 255;//化为无符号数
	X = X / 2;//将误差范围归一到[0,255]
	X = floor(X / pow(2, 8 - bits));
	X = X * pow(2, 8 - bits);
	X = OverflowX(X, 255, 0);
	return X;
}

int invErrorQuantity(int X, int bits)
{
	X = X * 2;//将误差放回原来的范围
	X = X - 255;//将无符号数转为有符号数
	return X;
}

void DPCM_Pixel(unsigned char* YOrigi, unsigned char* YError, unsigned char* YRestr, int pre, int j, int bits)
{
	/*for the first Pixel,suppose YError[0]=128-YOrigi[0],
	then quantify it and store*/
	int E_temp, R_temp;
	switch (j)
	{
	case 0:
		E_temp = 128 - YOrigi[pre + j];
		E_temp = ErrorQuantity(E_temp, bits);//quantity
		YError[pre + j] = E_temp;
		E_temp = invErrorQuantity(E_temp, bits);//inverse quantization
		R_temp = 128 - E_temp;
		R_temp = OverflowX(R_temp, 255, 0);//prevent overflow
		YRestr[pre + j] = (unsigned char)R_temp;
		break;
	default:
		E_temp = YOrigi[pre + j] - YRestr[pre + j - 1];
		E_temp = ErrorQuantity(E_temp, bits);//quantity
		YError[pre + j] = E_temp;
		E_temp = invErrorQuantity(E_temp, bits);//inverse quantization
		R_temp = E_temp + YRestr[pre + j - 1];
		R_temp = OverflowX(R_temp, 255, 0);//prevent overflow
		YRestr[pre + j] = (unsigned char)R_temp;
		break;
	}
}

void RDPCM_Pixel(unsigned char* YError, unsigned char* YRestr, int pre, int j, int bits)
{
	int E_temp, R_temp;
	switch (j)
	{
	case 0:
		E_temp = YError[pre + j];
		E_temp = invErrorQuantity(E_temp, bits);
		R_temp = 128 - E_temp;
		R_temp = OverflowX(R_temp, 255, 0);
		YRestr[pre + j] = (unsigned char)R_temp;
		break;
	default:
		E_temp = YError[pre + j];
		E_temp = invErrorQuantity(E_temp, bits);
		R_temp = E_temp + YRestr[pre + j - 1];
		R_temp = OverflowX(R_temp, 255, 0);
		YRestr[pre + j] = (unsigned char)R_temp;
		break;
	}
	
}

void DPCM(unsigned char* YOrigi, unsigned char* YError, unsigned char* YRestr, int height, int width, int bits)
{
	for (int i = 0; i < height; i++)
		for (int j = 0; j < width; j++)
			DPCM_Pixel(YOrigi, YError, YRestr, i * width, j, bits);//for each pixel,use the function DPCM_Pixel() to calculate the YRestr[i] and YError[i] 
}

void RDPCM(unsigned char* YError, unsigned char* YRestr, int height, int width, int bits)
{
	for (int i = 0; i < height; i++)
		for (int j = 0; j < width; j++)
			RDPCM_Pixel(YError, YRestr, i * width, j, bits);
}

double PSNR(unsigned char* YOrigi, unsigned char* YRestr, int height, int width)
{
	int fmax = pow(2, 8) - 1;
	int a = fmax * fmax;
	double mean_se = MSE(YOrigi, YRestr, height, width);
	double peak_SNR = 10 * log10((double)a / mean_se);
	return peak_SNR;
}

PSNR输出结果表

数据压缩(九)——DPCM压缩系统的实现与分析_第3张图片
   P S N R PSNR PSNR的结果总结成表格,结果如下:
数据压缩(九)——DPCM压缩系统的实现与分析_第4张图片
  可以发现,量化bit数越大,重建的图像质量越好,与常识相符。
  用 Y U V YUV YUV文件查看器打开文件进行验证。(由于图片数较多,以下就仅使用8bit、4bit、1bit量化的文件)

比特数 结果
8bit量化
4bit量化
1bit量化

  可以发现,8bit量化的时候基本可以还原原图像,而4bit量化就出现了轻微的块效应,1bit量化图像就完全不可用了,符合预期。

信源符号概率分布图

  由于要画概率分布图,在DPCM的main()函数代码中添加如下代码段,用来输出

void Pro(unsigned char* YOrigi, int Ysize)
{
	for (int i = 0; i < Ysize; i++)
		Probability[YOrigi[i]] = Probability[YOrigi[i]] + 1;
	for (int i = 0; i < 256; i++)
		Probability[i] = Probability[i] / 256;
}

  完整的main.cpp代码修改如下:

#include 
#include 
#include 
#include "DPCM_code.h"

using namespace std;

double Probability[256] = { 0 };
void Pro(unsigned char* YOrigi, int Ysize)
{
	for (int i = 0; i < Ysize; i++)
		Probability[YOrigi[i]] = Probability[YOrigi[i]] + 1;
	for (int i = 0; i < 256; i++)
		Probability[i] = Probability[i] / 256;
}

int main(int argc, char** argv)
{
	//moon图片464 538,使用的是4:4:4的yuv文件
	int width = atoi(argv[1]);
	int height = atoi(argv[2]);
	int bits = atoi(argv[3]);
	int Ysize = height * width;
	int Esize = height * width * 2;

	ifstream OrigiFile(argv[4], ios::binary);
	ofstream ErrorFile(argv[5], ios::binary);
	ofstream RestrFile(argv[6], ios::binary);
	ofstream ProbaFile(argv[7], ios::binary);
	if (!OrigiFile) { cout << "error to open OrigiFile!" << endl; }
	if (!ErrorFile) { cout << "error to open ErrorFile!" << endl; }
	if (!RestrFile) { cout << "error to open RestrFile!" << endl; }
	if (!ProbaFile) { cout << "error to open ProbaFile!" << endl; }

	unsigned char* YOrigi = new unsigned char[Ysize];
	unsigned char* YError = new unsigned char[Ysize];
	unsigned char* YRestr = new unsigned char[Ysize];
	unsigned char* E_File = new unsigned char[Esize];

	//读入Y分量和其余分量
	OrigiFile.read((char*)YOrigi, Ysize);
	OrigiFile.read((char*)E_File, Esize);

	//进行预测和量化
	DPCM(YOrigi, YError, YRestr, height, width, bits);
	
	//对重建图像进行重置,用以判断解码端工作效果
	for (int i = 0; i < Ysize; i++)
			YRestr[i] = 0;
	
	//从YError解出量化后的yuv文件
	RDPCM(YError, YRestr, height, width, bits);
	double peak_SNR = PSNR(YOrigi, YRestr, height, width);
	cout << peak_SNR << endl;

	//计算概率分布图
	if (argv[8][0] == 'O')
		Pro(YOrigi, Ysize);
	else if (argv[8][0] == 'E')
		Pro(YError, Ysize);

	//输出概率分布
	for (int i = 0; i < 256; i++)
	{
		ProbaFile << i << "\t" << Probability[i] << endl;
		//cout << i << "\t" << Probability[i] << endl;
	}

	ErrorFile.write((char*)YError, Ysize);
	ErrorFile.write((char*)E_File, Esize);
	RestrFile.write((char*)YRestr, Ysize);
	RestrFile.write((char*)E_File, Esize);

	OrigiFile.close();
	ErrorFile.close();
	RestrFile.close();
	delete[]YOrigi;
	delete[]YError;
	delete[]YRestr;
	delete[]E_File;
	return 0;
}

  为了简便起见,以4bit量化和8bit量化的error概率分布图和原图的概率分布图为例,命令行输入如下:
数据压缩(九)——DPCM压缩系统的实现与分析_第5张图片
  做出的图表如下(原文件,1bit量化,2bit量化,4bit量化,8bit量化):
数据压缩(九)——DPCM压缩系统的实现与分析_第6张图片

4.2 Huffman编码

压缩比输出结果表

  将8bit至1bit量化输出的E.yuv文件和原图HisSeed.yuv输入 H u f f m a n Huffman Huffman编码器。命令行输入如下:
数据压缩(九)——DPCM压缩系统的实现与分析_第7张图片
  输出是.huff文件和.txt文件。比较生成的.huff文件的文件大小。文件大小和压缩比计算如下表所示:
数据压缩(九)——DPCM压缩系统的实现与分析_第8张图片
  可以发现,进行了量化后的文件的压缩比随着量化比特数的增加而增大,即量化比特数越大,压缩效果越好。

(五)结果汇总

  以2bit量化、8bit量化为例。

bit数 error图查看 重建图查看 PSNR值 压缩比 信源符号概率分布图
2 数据压缩(九)——DPCM压缩系统的实现与分析_第9张图片 数据压缩(九)——DPCM压缩系统的实现与分析_第10张图片 12.0588dB 38.31% 数据压缩(九)——DPCM压缩系统的实现与分析_第11张图片
8 数据压缩(九)——DPCM压缩系统的实现与分析_第12张图片 数据压缩(九)——DPCM压缩系统的实现与分析_第13张图片 51.1307dB 83.06% 数据压缩(九)——DPCM压缩系统的实现与分析_第14张图片

你可能感兴趣的:(数据压缩原理与应用)