实验内容:将预测误差图像写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。最后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质量)。压缩质量以PSNR进行计算。
其中, d n d_n dn是预测误差, Q Q Q是量化器, P P P中存储的是重建值。
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 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;
}
}
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=0m−1Σj=0n−1[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:图像质量非常好(非常接近原始图像)
3020 PSNR<20:图像不可接受
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;
}
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;
}
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编码非常的实用。
老师给了一个.exe文件,用来存放 H u f f m a n Huffman Huffman编码的结果。
其中第一列是信源符号 a a a,第二列是信源符号出现的次数 b b b,第三列是编码的码字 c c c。
对原文件和DPCM编码后输出的error文件都进行此操作,即可判断DPCM+熵编码和只进行熵编码两种方案的区别,压缩比直接由.huff文件大小比上原.yuv文件大小计算得。
#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;
}
#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);
#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;
}
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
#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概率分布图和原图的概率分布图为例,命令行输入如下:
做出的图表如下(原文件,1bit量化,2bit量化,4bit量化,8bit量化):
将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.0588dB | 38.31% | |||
8 | 51.1307dB | 83.06% |