一、实验目的
掌握DPCM编解码系统的基本原理。初步掌握实验用C/C++/Python等语言编程实现DPCM编码器,并分析其压缩效率。
二、相关知识
1.DPCM编解码原理
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。
在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。
三、实验步骤
在本次实验中,我们采用固定预测器和均匀量化器。预测器采用左侧、上方预测均可。量化器采用8比特均匀量化。本实验的目标是验证DPCM编码的编码效率。首先读取一个256级的灰度图像,采用自己设定的预测方法计算预测误差,并对预测误差进行8比特均匀量化(基本要求)。还可对预测误差进行1比特、2比特和4比特的量化设计(提高要求)。
在DPCM编码器实现的过程中可同时输出预测误差图像和重建图像。将预测误差图像写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。
将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。最后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质量)。压缩质量以PSNR进行计算。
四、实验代码
DPCM.cpp
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int i = 8; //做i bit的量化
//量化器
unsigned char Q(int error)
{
unsigned char pre_e;
pre_e = error / pow(2, 9 - i);
return pre_e;
}
//反量化器
unsigned char QT(unsigned char pre_e)
{
unsigned char pre_eT;
pre_eT = pre_e * pow(2, 9 - i);
return pre_eT;
}
//DPCM
void DPCM(unsigned char* origin, unsigned char* pre_e, unsigned char* rebuild, int w, int h)
{
int* error;
error = (int*)calloc(w * h, sizeof(int));
//int error[w*h] = { 0 };
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
rebuild[w * j] = origin[w * j];
pre_e[w * j] = 0;
if (i > 0)
{
error[i] = origin[w * j + i] - rebuild[w * j + i - 1];//预测误差
pre_e[w * j + i] = Q(error[i]);
QT(pre_e[w * j + i]);
rebuild[w * j + i] = rebuild[w * j + i - 1] + QT(pre_e[w * j + i]);
}
}
}
for (int i = 0; i < w * h; i++)
{
pre_e[i] = pre_e[i] + 128;
if (pre_e[i] > 255)pre_e[i] = 255;
}
for (int i = 0; i < w * h; i++)
{
if (rebuild[i] > 255)rebuild[i] = 255;
if (rebuild[i] < 0)rebuild[i] = 0;
}
}
main.cpp
#include <stdio.h>
#include <windows.h>
#include "bmp2yuv.h"
void main(int argc, char* argv[])
{
FILE* bmpFile = NULL, * yuvFile = NULL;
BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;
char* bmpFileName = NULL;
char* yuvFileName = NULL;
unsigned char* rgbBuf = NULL;
unsigned char* yBuff = NULL;
unsigned char* uBuff = NULL;
unsigned char* vBuff = NULL;
int flip = 0;//flip=1时正序,为0时上下翻转
int width, height;
//打开yuv文件
if ((yuvFile = fopen(argv[2], "wb+")) == NULL)
{
printf("yuv file failed!");
exit(0);
}
else
{
yuvFile = fopen(argv[2], "wb+");
printf("The output yuv file is %s\n", yuvFile);
}
// 打开bmp文件
if ((bmpFile = fopen(argv[1], "rb+")) == NULL)
{
printf("bmp file open failed!");
exit(0);
}
else
{
bmpFile = fopen(argv[1], "rb+");
printf("The input bmp file is %s\n", bmpFile);
}
// 读文件头信息
if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
{
printf("read file header error!");
exit(0);
}
//判断是否为bmp文件
if (File_header.bfType != 0x4D42)
{
printf("Not bmp file!");
exit(0);
}
//读信息头信息
if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
{
printf("read info header error!");
exit(0);
}
width = Info_header.biWidth;
height = Info_header.biHeight;
printf("This is a %d bits image!\n", Info_header.biBitCount);
printf("\nbmp size: \t%d X %d\n", Info_header.biWidth, Info_header.biHeight);
//开辟4个缓冲区
rgbBuf = (unsigned char*)malloc(height * width * 3);
memset(rgbBuf, 0, height * width * 3);//初始化函数,用于清0
yBuff = (unsigned char*)malloc(height * width);
uBuff = (unsigned char*)malloc((height * width) / 4);
vBuff = (unsigned char*)malloc((height * width) / 4);
ReadRGB(File_header, Info_header, bmpFile, rgbBuf);
if (RGB2YUV(width, height, rgbBuf, yBuff, uBuff, vBuff, flip))
{
printf("rgb2yuv error");
exit(1);
}
for (int i = 0; i < width * height; i++)
{
if (yBuff[i] < 16) yBuff[i] = 16;
if (yBuff[i] > 235) yBuff[i] = 235;
}
for (int i = 0; i < width * height / 4; i++)
{
if (uBuff[i] < 16) uBuff[i] = 16;
if (uBuff[i] > 240) uBuff[i] = 240;
if (vBuff[i] < 16) vBuff[i] = 16;
if (vBuff[i] > 240) vBuff[i] = 240;
}
fwrite(yBuff, 1, width * height, yuvFile);
fwrite(uBuff, 1, (width * height) / 4, yuvFile);
fwrite(vBuff, 1, (width * height) / 4, yuvFile);
//DPCM
//只对y通道进行操作
FILE* predictPic = NULL, * rebuildPic = NULL;
if ((predictPic = fopen(argv[3], "wb+")) == NULL)
{
printf("predict.yuv open fail!\n");
exit(0);
}
if ((rebuildPic = fopen(argv[4], "wb+")) == NULL)
{
printf("rebuild.yuv open fail!\n");
exit(0);
}
//unsigned char *origin = NULL;
unsigned char* pre_e = NULL;
unsigned char* rebuild = NULL;
//origin = (unsigned char *)malloc(width*height* sizeof(char));
pre_e = (unsigned char*)malloc(width * height * sizeof(char));
rebuild = (unsigned char*)malloc(width * height * sizeof(char));
DPCM(yBuff, pre_e, rebuild, width, height);
unsigned char* uv;
uv = (unsigned char*)malloc(width * height / 2 * sizeof(char));
for (int i = 0; i < width * height / 2; i++)
{
uv[i] = 128;
}
fwrite(pre_e, 1, width * height, predictPic);
fwrite(uv, 1, (height * width) / 2, predictPic);
fwrite(rebuild, 1, width * height, rebuildPic);
fwrite(uBuff, 1, (height * width) / 4, rebuildPic);
fwrite(vBuff, 1, (height * width) / 4, rebuildPic);
//free(origin);
free(pre_e);
free(rebuild);
fclose(predictPic);
fclose(rebuildPic);
free(rgbBuf);
free(yBuff);
free(uBuff);
free(vBuff);
fclose(bmpFile);
fclose(yuvFile);
}
dealdata.cpp
#include <stdio.h>
#include <math.h>
#include <windows.h>
#define w 256
#define h 256
#define bit 8 //量化比特数
void main(int argc, char* argv[])
{
FILE* dataFile = NULL, * processFile = NULL, * rebuild = NULL, * origin = NULL;
//打开文件
if ((dataFile = fopen(argv[1], "rb")) == NULL)
{
printf("data file failed!");
exit(0);
}
if ((processFile = fopen(argv[2], "wb")) == NULL)
{
printf("process file failed!");
exit(0);
}
if ((rebuild = fopen(argv[3], "rb")) == NULL)
{
printf("rebuild file failed!");
exit(0);
}
if ((origin = fopen(argv[4], "rb")) == NULL)
{
printf("origin file failed!");
exit(0);
}
unsigned char* originData = NULL;
int* processData = NULL;
originData = (unsigned char*)malloc(w * h * 1.5);
processData = (int*)calloc(256, sizeof(int));
fread(originData, 1, w * h * 1.5, dataFile);
for (int i = 0; i < w * h * 1.5; i++)
{
for (int j = 0; j < 256; j++)
{
if (originData[i] == j)
processData[j]++;
}
}
for (int i = 0; i < 256; i++)
{
fprintf(processFile, "%d\n", processData[i]);
}
//PSNR
unsigned char* orpic = NULL;
unsigned char* repic = NULL;
orpic = (unsigned char*)malloc(w * h * 1.5);
repic = (unsigned char*)malloc(w * h * 1.5);
fread(orpic, 1, w * h * 1.5, origin);
fread(repic, 1, w * h * 1.5, rebuild);
double MSE;
double k = 0;
for (int i = 0; i < w * h * 1.5; i++)
{
k = pow(orpic[i] - repic[i], 2) + k;
}
MSE = k / (w * h * 1.5);
double psnr;
double a;
a = pow(pow(2, bit) - 1, 2);
psnr = 10 * log10(a / MSE);
printf("psnr(%dbit)=%lf\n", bit, psnr);
free(orpic);
free(repic);
free(originData);
free(processData);
fclose(dataFile);
fclose(processFile);
fclose(origin);
fclose(rebuild);
}
五、实验结果
8bit量化输出结果:
将预测误差图像、原始图像文件写入文件并将该文件输入Huffman编码器并输出。
压缩比:
压缩质量分析(PSNR):
公式
计算得:PSNR=49.9dB
概率分布图: