DPCM压缩系统的实现和分析
掌握DPCM编解码系统的基本原理。初步掌握实验用C/C++/Python等语言编程实现DPCM编码器,并分析其压缩效率。
1、DPCM编解码原理
差分预测编码是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器。
2、DPCM编码系统的设计
在本次实验中,我们采用固定预测器和均匀量化器。预测器采用左侧、上方预测均可。量化器采用8比特均匀量化。
main.cpp
#include
#include
#include
#include
#include
#include"DPCM.h"
int main(int argc, char** argv)
{
int frameWidth;//图像的宽和高
int frameHeight;
/* internal variables */
char* yFileName = NULL;//原文件
char* recFileName = NULL;//重建值文件
char* errFileName = NULL;//量化后的预测误差
FILE* yFile = NULL;
FILE* recFile = NULL;
FILE* errFile = NULL;
unsigned char * yBuf = NULL;
unsigned char * uBuf = NULL;
unsigned char * vBuf = NULL;
unsigned char * recBuf = NULL;
unsigned char * errBuf = NULL;
yFileName = argv[1];
recFileName = argv[2];
errFileName = argv[3];
frameWidth = atoi(argv[4]);
frameHeight = atoi(argv[5]);
/* open the YUV file */
fopen_s(&yFile,yFileName, "rb");
if (yFile == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
/* open the restruction file */
recFile = fopen(recFileName, "wb");
if (recFile == NULL)
{
printf("cannot find rec file\n");
exit(1);
}
/* open the difference file */
errFile = fopen(errFileName, "wb");
if (errFile == NULL)
{
printf("cannot find error file\n");
exit(1);
}
/* get the output buffers for a frame */
yBuf = (unsigned char*)malloc(frameWidth * frameHeight * sizeof(unsigned char));
uBuf = (unsigned char*)malloc(frameWidth * frameHeight * sizeof(unsigned char)/4);
vBuf = (unsigned char*)malloc(frameWidth * frameHeight * sizeof(unsigned char)/4);
recBuf = (unsigned char*)malloc(frameWidth * frameHeight * sizeof(unsigned char));
errBuf = (unsigned char*)malloc(frameWidth * frameHeight * sizeof(unsigned char) * 1.5);
if (yBuf == NULL || recBuf == NULL || errBuf == NULL || uBuf == NULL ||vBuf == NULL)
{
printf("wrong\n");
exit(1);
}
fread(yBuf, 1, frameWidth * frameHeight, yFile);
if (yBuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
DPCMLeft(frameWidth,frameHeight,yBuf,recBuf,errBuf);
/*写u v*/
for(int i=0;i<frameHeight/2;i++)
{
for(int j=0;j<frameWidth/2;j++)
{
*(uBuf+j+i*frameWidth/2)=128;
*(vBuf+j+i*frameWidth/2)=128;
}
}
/*写入重建和误差图像*/
fwrite(recBuf, 1, frameWidth * frameHeight, recFile);
fwrite(errBuf, 1, frameWidth * frameHeight, errFile);
fwrite(uBuf, 1, frameWidth * frameHeight/4, errFile);
fwrite(vBuf, 1, frameWidth * frameHeight/4, errFile);
/*PSNR*/
simplest_yuv420_psnr(yBuf,recBuf,frameWidth,frameHeight,1);
/* cleanup */
free(yBuf);
free(recBuf);
free(errBuf);
free(uBuf);
free(vBuf);
fclose(yFile);
fclose(recFile);
fclose(errFile);
system("pause");
return(0);
}
DPCM.h
void DPCMLeft(int Width,int Height,void *yBuff,void *recBuff,void *errBuff);
int simplest_yuv420_psnr(void *yBuff1,void *yBuff2, int w, int h, int num);
DPCM.cpp
#include
#include
#include
#include
#include
#include"DPCM.h"
//向左预测//
void DPCMLeft(int Width,int Height,void *yBuff,void *recBuff,void *errBuff)//DPCM向左预测
{
unsigned char *yB=NULL;
yB = (unsigned char *)yBuff;
unsigned char *recB=NULL;
recB = (unsigned char *)recBuff;
unsigned char *errB=NULL;
errB = (unsigned char *)errBuff;
int P1,P2;//P1为当前值与预测值的误差,P2为量化后的误差
unsigned char P3;//P3为反量化后的误差
for(int i=0;i<Height;i++)
{
for(int j=0;j<Width;j++)
{
if(j == 0)//向左进行预测时,图像最左边一列的像素值直接输出,无需进行差分预测
{
*(recB+j+i*Width)=*(yB+j+i*Width);//当前值即为重建值,作为下一个像素的参考值
*(errB+j+i*Width)=0;//误差为0
}
else//当不是最左边一列的像素时,进行DPCM
{
P1=*(yB+j+i*Width)-*(recB+(j-1)+i*Width);//求当前值与参考值的差值
if(P1%2==0)//对差值进行8bit均匀量化,并进行+128的偏移以输出
P2=P1/2+128;
else
P2=(P1-1)/2+128;
*(errB+j+i*Width)=unsigned char(P2);//将误差写入errB缓存区域
P3=unsigned char(P2*2);//对量化后的误差反量化
*(recB+j+i*Width)=*(recB+(j-1)+i*Width)+P3;
//将参考值与反量化得到的误差相加,作为当前像素的重建值,即下一个像素的参考值
}
}
}
}
//PSNR//
int simplest_yuv420_psnr(void *yBuff1,void *yBuff2, int w, int h, int num)//计算Y分量的PSNR
{
unsigned char *yB1=NULL;
yB1 = (unsigned char *)yBuff1;
unsigned char *yB2=NULL;
yB2 = (unsigned char *)yBuff2;
for (int i = 0; i < num; i++)
{
double mse_sum = 0, mse = 0, psnr = 0;
for (int j = 0; j < h ; j++)
{
for (int k = 0; k < w; k++)
{
mse_sum += pow((double)(*(yB1+k+j*w) - *(yB2+k+j*w)), 2);//取每个差值的平方,并进行累加
}
}
mse = mse_sum / (w * h); //根据公式计算mse
psnr = 10 * log10(255.0 * 255.0 / mse); //根据公式计算psnr
printf("%5.3f\n", psnr);
}
system("pause");
return 0;
}
此处的概率分布使用作业一的yuv文件概率分布程序得出:
熵编码 | DPCM+熵编码 | |
---|---|---|
原文件大小 | 98304字节 | 98304字节 |
文件大小 | 69885字节 | 45410 字节 |
编码效率(压缩比) | 0.71 | 0.46 |
压缩质量(PSNR) | 51.177 |
YUV文件 | 概率分布 |
---|---|
原图![]() |
![]() |
误差图片![]() |
![]() |
结论: