DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,
需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是
因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实
际内嵌了一个解码器,如编码器中虚线框中所示。
在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器
和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和
量化器的优化设计。
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是"Peak Signal to Noise Ratio"的缩写,即峰值信噪比,是一种评价图像的客观标准,它具有局限性,一般是用于最大值信号和背景噪音之间的一个工程项目。其公式如下:
其中,MSE是原图像与处理图像之间均方误差,其公式为:
Peak就是指8bits表示法的最大值255。MSE指MeanSquareError,I(角标n)指原始影像第n个pixel值,P(角标n)指经处理后的影像第n个pixel值。PSNR的单位为dB。所以PSNR值越大,就代表失真越少。
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;
}
原图 seed.yuv:
分别查看1bit、2bit、4bit、5bit、8bit量化下的误差图像与重建图像。经实验发现4bit以下量化会带来较大的误差,图像难以还原,而5bit以上的量化误差较小,量化比特越大,误差越小。
比特数 | 误差图像 | 重建图像 |
---|---|---|
1bit | ||
2bit | ||
4bit | ||
5bit | ||
8bit |
输出码流的概率分布图与压缩质量PSNR如下,可以看到量化比特数越大,PSNR峰值信噪比越大,即图像压缩质量越好。
bit数 | PSNR | 概率分布 |
---|---|---|
原图 | — | |
1bit | 8.23 | |
2bit | 8.53 | |
3bit | 10.99 | 略 |
4bit | 14.54 | |
5bit | 25.23 | 略 |
6bit | 26.86 | 略 |
7bit | 27.77 | 略 |
8bit | 30.27 |
在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编码后,文件量化比特数越大,压缩比越大,压缩效果越好。
输入文件量化比特 | 文件大小/kb | 压缩比 |
---|---|---|
源文件 | 248 | 33.83% |
1bit | 123 | 16.78% |
2bit | 134 | 18.28% |
3bit | 137 | 18.69% |
4bit | 141 | 19.24% |
5bit | 153 | 20.87% |
6bit | 170 | 23.19% |
7bit | 178 | 24.28% |
8bit | 207 | 28.24% |
//dpcm.cpp
#include
#include "rgb2yuv.h"
#include
#include
#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;
}