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

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


(一)DPCM编解码原理

在这里插入图片描述
  其中, 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<PSNR<40dB:图像质量是好的(失真可以察觉但可以接受)
20<PSNR<30dB:图像质量差
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

完整代码

  解决方案资源管理器如下:
在这里插入图片描述

main.cpp
#include <iostream>
#include <cstdio>
#include <fstream>
#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 <iostream>
#include <cstdio>
#include <fstream>
#include <cmath>
#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输出结果表

在这里插入图片描述
   P S N R PSNR PSNR的结果总结成表格,结果如下:
在这里插入图片描述
  可以发现,量化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 <iostream>
#include <cstdio>
#include <fstream>
#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概率分布图和原图的概率分布图为例,命令行输入如下:
在这里插入图片描述
  做出的图表如下(原文件,1bit量化,2bit量化,4bit量化,8bit量化):
在这里插入图片描述

4.2 Huffman编码

压缩比输出结果表

  将8bit至1bit量化输出的E.yuv文件和原图HisSeed.yuv输入 H u f f m a n Huffman Huffman编码器。命令行输入如下:
在这里插入图片描述
  输出是.huff文件和.txt文件。比较生成的.huff文件的文件大小。文件大小和压缩比计算如下表所示:
在这里插入图片描述
  可以发现,进行了量化后的文件的压缩比随着量化比特数的增加而增大,即量化比特数越大,压缩效果越好。

(五)结果汇总

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

bit数error图查看重建图查看PSNR值压缩比信源符号概率分布图
2在这里插入图片描述在这里插入图片描述12.0588dB38.31%在这里插入图片描述
8在这里插入图片描述在这里插入图片描述51.1307dB83.06%在这里插入图片描述
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DPCM(差分脉冲编码调制)是一种数据压缩系统,其目的是在尽量不失真的情况下减少数据存储或传输所需的带宽。DPCM 压缩系统实现主要是在传输前对原始信号进行差分编码,将每一个样本值与它前面的样本值的差值编码传输。在解码端,将编码的差值与前一个样本值相加,得到解码后的样本值。 DPCM 压缩系统的性能取决于差分编码器的性能和采样率。通常,差分编码器需要对原始信号进行线性预测,并将其与实际样本值的差异编码。预测器的设计和参数设置是影响性能的关键因素。 DPCM 压缩系统主要有以下优点:首先,DPCM 采用差分编码,可以将一系列相邻的样本压缩成差分值,从而减少需要传输的数据量。其次,由于 DPCM 是一种有损压缩策略,可以通过控制压缩后的失真来有效地减少数据量。再者,DPCM 压缩系统的计算和编码速度较快,可以在硬件中实现,适用于实时视频或音频传输。 然而,DPCM 压缩系统也有一些缺点。首先,差分编码容易受到干扰和噪声的影响,从而导致误差传播和失真的增加。此外,DPCM 不适用于大多数不规则信号,如图片和文本等。 总之,DPCM 压缩系统是一种有损压缩策略,可以通过对原始信号进行差分编码来减少数据量。其性能主要取决于差分编码器的设计和采样率,适用于实时音视频传输和存储等场景。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值