一、实验目的
掌握DPCM编解码系统的基本原理。初步掌握实验用C/C++/Python等语言编程实现DPCM编码器,并分析其压缩效率。
二、DPCM编解码原理
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。
在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。
三、PSNR原理
PSNR 是“Peak Signal to Noise Ratio”的缩写,即峰值信噪比,是一种评价图像的客观标准,它具有局限性,一般是用于最大值信号和背景噪音之间的一个工程项目。
其数学公式如下图所示:
其中,MSE是原图像(语音)与处理图像(语音)之间均方误差。
四、实验代码
头文件:
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;
}
main函数:
#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;
}
参数设置:
五、实验结果
1、输出图像
图像顺序依次为:原始图像、 预测误差图像、重建图像
8bit量化
4bit量化
2bit量化
1bit量化
量化比特数从8bit到1bit,重建图像质量逐渐下降。
2、计算PSNR值
量化比特数 | PSNR值 |
---|---|
8bit | 51.1338 |
4bit | 23.0725 |
2bit | 11.8881 |
1bit | 9.86284 |
六、Huffman编码
在cmd命令提示符中输入命令行:
得到输出文件:
七、计算、比较压缩比
量化比特数 | 压缩比 |
---|---|
原始图像 | 71.9% |
8bit | 36.5% |
4bit | 23.9% |
2bit | 21.9% |
1bit | 21.4% |
量化后文件的压缩比随着量化比特数的增加而增大,量化比特数越大,压缩效果越好。