DPCM 压缩系统的实现和分析
原理
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。
在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。
内容
利用左侧像素进行线性预测,量化器采用8bit均匀量化和4bit均匀量化
并对压缩后的文件和压缩质量进行分析
代码设计
#include<stdio.h>
#include< stdlib.h>
#include<iostream>
using namespace std;
void Qualify_8bit(unsigned char a, unsigned char b, unsigned char& e, unsigned char& y) {
int e1 = a - b;
e = e1 / 2 + 128;
y = b + (e -128) * 2;
e = (e - 128) * 2 + 128;//提升电平
return;
}
void Qualify_4bit(unsigned char a,unsigned char b,unsigned char &e,unsigned char &y) {
int e1 = a - b;
e = e1 / 32 + 16;
y = b + (e - 16) * 32;
e = (e-16)*32+128;//提升电平
return;
}
int main(int argc, char* argv[]) {
//argv[1]为文件名
//argv[2][3]为宽高
//argv[4]:重建文件名
//argv[5]:差值文件名
FILE* inYuvFileP = NULL;
if (fopen_s(&inYuvFileP, argv[1], "rb") == 0) {
cout << "open inputYUVFile successfully!" << endl;
fseek(inYuvFileP, 0, SEEK_SET);
}
FILE* preYuvFileP = NULL;
if (fopen_s(&preYuvFileP, argv[4], "wb") == 0) {
cout << "open predYUVFile successfully!" << endl;
}
FILE* eFileP = NULL;
if (fopen_s(&eFileP, argv[5], "wb") == 0) {
cout << "open eFile successfully!" << endl;
}
int W = atoi(argv[2]);
int H = atoi(argv[3]);
int imgSize = W * H;
unsigned char* Ybuf = (unsigned char*)malloc(sizeof(unsigned char)*imgSize);
unsigned char* Ubuf = (unsigned char*)malloc(sizeof(unsigned char) * imgSize/4);
unsigned char* Vbuf = (unsigned char*)malloc(sizeof(unsigned char) * imgSize/4);
unsigned char*preYbuf= (unsigned char*)malloc(sizeof(unsigned char)*imgSize);
unsigned char* ebuf = (unsigned char*)malloc(sizeof(unsigned char) * imgSize);
fread(Ybuf, imgSize, sizeof(unsigned char), inYuvFileP);
//相减
int e1 = 0;
for (int i = 0; i < H; i++) {
//e = Qualify_8bit(e1);
//ebuf[i*W] = e;//固定第一列与128相减得到差值
//preYbuf[i * W ] = 128 + (ebuf[i * W]-128)*2;
unsigned char e = 0;
unsigned char y = 0;
Qualify_8bit(Ybuf[i * W], 128,e,y);
ebuf[i * W] = e;
preYbuf[i * W] = y;
for (int j = 1; j < W; j++) {
//e1 = Ybuf[i * W + j] -preYbuf[i * W + j - 1];
//e = Qualify_8bit(e1);
//ebuf[i * W + j] = e;
防止溢出判断
//y = preYbuf[i * W + j - 1] + (e - 128) * 2;
Qualify_8bit(Ybuf[i * W+j],preYbuf[i*W+j-1],e,y);
ebuf[i * W+j] = e;
cout << e << " ";
//其实没有必要!!!!判断
if ( y> 255) { y = 255; }
if (y < 0) { y = 0; }
//
preYbuf[i * W + j] = y;
//cout << preYbuf[i * W + j] << " ";
}
}
for (int i = 0; i < W * H / 4; i++) {
Ubuf[i] = 128;
Vbuf[i] = 128;
}
fwrite(preYbuf, sizeof(unsigned char), imgSize, preYuvFileP);
fwrite(Ubuf, sizeof(unsigned char), imgSize/4, preYuvFileP);
fwrite(Vbuf, sizeof(unsigned char), imgSize/4, preYuvFileP);
fwrite(ebuf, sizeof(unsigned char), imgSize, eFileP);
fwrite(Ubuf, sizeof(unsigned char), imgSize / 4, eFileP);
fwrite(Vbuf, sizeof(unsigned char), imgSize / 4, eFileP);
free( Ybuf);
free(Ubuf);
free(Vbuf);
free(ebuf);
free(preYbuf);
fclose(inYuvFileP);
fclose(preYuvFileP);
fclose(eFileP);
}
还是对C++语法不熟,遇见了不少困难。
逻辑上主要出现的问题只有一个问题
如何显示我们实际上传输的图像??
我思考了挺久,如果将相减后的量化值直接存于预测误差图像,结果基本全黑或全灰(单纯提升128电平后),但实际上接收端收到的信息应该是(e-16)*32+128,所以将这个存入预测误差图像就是我们实际上传输的图像。
每行第一列像素我都默认以128为基准
实验效果
4bit量化
8bit量化
经过哈夫曼编码后的效果
8bit量化:原Lena.yuv从96K到69K
压缩后的:原Lena.yuv从96K到43K
因为4bit量化个人觉得失真度太大,就不计算了
PSNR计算
峰值信噪比(PSNR), 一种评价图像的客观标准。它具有局性,PSNR是“Peak Signal to Noise Ratio”的缩写。peak的中文意思是顶点。而ratio的意思是比率或比列的。整个意思就是到达噪音比率的顶点信号,psnr一般是用于最大值信号和背景噪音之间的一个工程项目。通常在经过影像压缩之后,通常输 出的影像都会在某种程度与原始影像不同。为了衡量经过处理后的影像品质,我们通常会参考PSNR值来衡量某个处理程序能否令人满意。它是原图像与被处理图 像之间的均方误差相对于(2n-1)2的对数值(信号最大值的平方,n是每个采样值的比特数),它的单位是dB
PSNR高于40dB说明图像质量极好(即非常接近原始图像),
在30—40dB通常表示图像质量是好的(即失真可以察觉但可以接受),
在20—30dB说明图像质量差;
最后,PSNR低于20dB图像不可接受
8bit量化后结果为:51.265dB
PSNR计算:
float calPSNR(unsigned char* Ybuf, unsigned char* predYbuf,int n) {
float a = 0;
unsigned char max = 255;
for (int i = 0; i < n; i++) {
a = a+(Ybuf[i] - predYbuf[i]) * (Ybuf[i] - predYbuf[i]);
}
float mse = a / n;
float tmp = max * max / mse;
float psnr = 10 * log10(tmp);
return psnr;
}