DPCM压缩系统的实现和分析
目录
实验原理
DPCM
是差分预测编码,它是一个负反馈系统,其编解码原理如下图:
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中, 需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是 因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实 际内嵌了一个解码器,如编码器中虚线框中所示。 在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器 和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和 量化器的优化设计。
在本次实验中,我们采用固定预测器和均匀量化器。预测器采用左侧、上方预测均可。量化器采用8比特均匀量化。本实验的目标是验证DPCM编码的编码效率。首先读取一个 256级的灰度图像,采用自己设定的预测方法计算预测误差,并对预测误差进行8比特均匀 量化(基本要求)。还可对预测误差进行1比特、2比特和4比特的量化设计(提高要求)。
在DPCM编码器实现的过程中可同时输出预测误差图像和重建图像。将预测误差图像 写入文件并将该文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。 将原始图像文件输入Huffman编码器,得到输出码流、给出概率分布图并计算压缩比。最 后比较两种系统(1.DPCM+熵编码和2.仅进行熵编码)之间的编码效率(压缩比和图像质 量)。压缩质量以PSNR进行计算。
PSNR计算压缩质量
psnr是“Peak Signal to Noise Ratio”的缩写,即峰值信噪比,是一种评价图像的客观标准。它是原图像与被处理图像之间的均方误差相对于(2n-1)2的对数值(信号最大值的平方,n是每个采样值的比特数),它的单位是dB。PSNR值越大,就代表失真越少。其中,MSE是原图像(语音)与处理图像(语音)之间均方误差。
实验代码
DPCM.cpp
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
void DPCM(unsigned char* y_buffer, unsigned char* differ_buffer, unsigned char* rebuild_buffer,int width, int height,int bitnum)
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (j == 0)
{
differ_buffer[i * width] = ((y_buffer[i * width] - 128)+255)/pow(2,(double)(9-bitnum));
rebuild_buffer[i * width] = differ_buffer[i * width] * pow(2, (double)(9 - bitnum)) - 255 + 128;
}
else
{
differ_buffer[i * width+j] = ((y_buffer[i * width+j] - rebuild_buffer[i * width+j-1]) + 255) / pow(2, (double)(9 - bitnum));
rebuild_buffer[i * width + j] = differ_buffer[i * width+j] * pow(2, (double)(9 - bitnum)) - 255 + rebuild_buffer[i * width + j-1];
}
if (differ_buffer[i * width + j] > 255)
differ_buffer[i * width + j] = 255;
if(differ_buffer[i * width + j] <0)
differ_buffer[i * width + j] = 0;
if (rebuild_buffer[i * width + j] > 255)
rebuild_buffer[i * width + j] = 255;
if (rebuild_buffer[i * width + j] < 0)
rebuild_buffer[i * width + j] = 0;
}
}
}
main.cpp
#include<stdlib.h>
#include<stdio.h>
#include"math.h"
#include"DPCM.h"
void calculate_fre(unsigned char* buffer, double* frequency, int width, int height)
{
int size = width * height;
for (int i = 0; i < size; i++)
{
frequency[buffer[i]]++;
}
for (int k = 0; k < 256; k++)
{
frequency[k] /= size;
}
}
int main(int argc, char** argv)
{
const char* ori_name = argv[1];
const char* differ_name = argv[2];
const char* rebu_name = argv[3];
int bitnum = atoi(argv[4]);
FILE* ori_file = NULL;
FILE* differ_file = NULL;
FILE* rebu_file = NULL;
if ((ori_file = fopen(ori_name, "rb")) == NULL)
printf("Failed to open the original picture\n");
else
printf("succeeded to open the original picture\n");
if ((differ_file = fopen(differ_name, "wb")) == NULL)
printf("Failed to open the difference picture\n");
else
printf("succeeded to open the difference picture\n");
if ((rebu_file = fopen(rebu_name, "wb")) == NULL)
printf("Failed to open the rebulid picture\n");
else
printf("succeeded to open the rebulid picture\n");
int width = 256;
int height = 256;
unsigned char* y_buffer = new unsigned char[width * height];
unsigned char* u_buffer = new unsigned char[width * height / 4];
unsigned char* v_buffer = new unsigned char[width * height / 4];
unsigned char* differ_buffer = new unsigned char[width * height];
unsigned char* rebuild_buffer = new unsigned char[width * height];
fread(y_buffer, 1, width * height, ori_file);
fread(u_buffer, 1, width * height / 4, ori_file);
fread(v_buffer, 1, width * height / 4, ori_file);
//计算原图像的概率分布
double frequency[256] = { 0 };
calculate_fre(y_buffer, frequency, width, height);
FILE* orin_fre;
orin_fre = fopen("ori_frequency.txt", "wb");
fprintf(orin_fre, "%s\t%s\t", "symbol", "freq");
for (int i = 0; i < 256; i++)
{
fprintf(orin_fre, "%d\t%f\t", i, frequency[i]);
}
DPCM(y_buffer, differ_buffer, rebuild_buffer,width,height,bitnum);
//计算预测误差的概率分布
double frequency2[256] = { 0 };
calculate_fre(differ_buffer, frequency2, width, height);
FILE* differ_fre;
differ_fre = fopen("differ_frequency.txt", "wb");
fprintf(differ_fre, "%s\t%s\t", "symbol", "freq");
for (int i = 0; i < 256; i++)
{
fprintf(differ_fre, "%d\t%f\t", i, frequency2[i]);
}
//写入预测误差图像
fwrite(differ_buffer,1,width*height,differ_file);
fwrite(u_buffer, 1, width * height/4, differ_file);
fwrite(v_buffer, 1, width * height / 4, differ_file);
//写入重建图像
fwrite(rebuild_buffer, 1, width * height, rebu_file);
fwrite(u_buffer, 1, width * height / 4, rebu_file);
fwrite(v_buffer, 1, width * height / 4, rebu_file);
// 计算PSNR
double mse = 0;
double psnr = 0;
for (int i = 0; i < width*height; i++) {
mse += pow((y_buffer[i]-rebuild_buffer[i]),2);
}
mse = mse / (width * height);
psnr = 10 * log10(pow(255,2) / mse);
printf("PSNR=%f", psnr);
fclose(ori_file);
fclose(differ_file);
fclose(rebu_file);
delete[] y_buffer;
delete[] u_buffer;
delete[] v_buffer;
delete[] differ_buffer;
delete[] rebuild_buffer;
return 0;
}
DPCM.h
#pragma once
#ifndef DPCM_H_
#define DPCM_H_
void DPCM(unsigned char* y_buffer, unsigned char* differ_buffer, unsigned char* rebuild_buffer, int width, int height, int bitnum);
#endif
实验步骤
实验图片的准备
修改之前的BMP2YUV工程文件,将实验所给bmp文件转换为yuv文件。
除了Birds.yuv
的大小是768×512以外,其余所有图片大小均为256×256
命令参数
在项目属性页更改命令参数,运行程序,得到8bit量化后的预测图像和重建图像。
图像质量比较
8比特量化
8bit量化时图像质量比较(以PSNR进行计算)
原图 | 预测误差 | 重建图像 | PSNR |
51.133820 | |||
51.142562 | |||
44.182261 | |||
14.886160 | |||
18.241523 | |||
17.084274 | |||
51.135541 |
我们可以看到,当用8bit量化时,1-3和最后一张重建图像的PSNR均大于40dB,说明图像质量很好,人眼很难看出差异,而4-6的PSNR均小于20dB,说明图像质量很差,人眼可以很明显的看出差异.
4比特,2比特,1比特量化
下面我们来比较对于一张相同的图片,分别用8bit,4bit,2bit,1bit量化时,所得重建图像的差别
量化比特数 | 原图 | 预测误差 | 重建图 | PSNR |
8 | 51.133820 | |||
4 | 14.818887 | |||
2 | 7.649990 | |||
1 | 7.598916 |
随着量化bit数减少,PSNR值也逐渐降低,图像出现了明显的失真,已经无法很好的重建图像了
Huffman熵编码
用给定的huff_run程序进行huffman编码,在命令行窗口输入:
得到编码后的huff文件和概率统计文本文件
比较编码效率
分别对原始yuv文件和预测误差文件进行哈夫曼编码:
原始图像 | 原始图像大小(KB) | 直接熵编码(KB) | 压缩效率 | DPCM+熵编码(KB) | 压缩效率 |
Lena.yuv | 96 | 68.2 | 140% | 45 | 213% |
Clown.yuv | 96 | 73.4 | 130% | 47.4 | 202% |
Fruit.yuv | 96 | 73.9 | 130% | 41.8 | 229% |
Noise.yuv | 96 | 70.3 | 136% | 74.1 | 129% |
Odie.yuv | 96 | 20.3 | 473% | 17.7 | 542% |
Zone.yuv | 96 | 73.7 | 130% | 43.5 | 220% |
Birds.yuv | 576 | 520 | 111% | 192 | 300% |
可以看出,经过DPCM后再进行熵编码,一般来说,压缩效率比直接熵编码大大提高。对于个别图像,经过DPCM后再进行熵编码,压缩效率反而下降。可能是由于图像水平方向的相关性太低,不适合做预测
原始图像与预测误差概率分布
图像 | 原始图像概率分布 | 预测误差概率分布 |
Lena.yuv | ||
Clown.yuv | ||
Fruit.yuv |
通过观察各个图像的概率分布图可以发现,利用DPCM得到的预测残差图的概率分布近似为Laplacian分布,更适合用霍夫曼编码,因此预测误差图的压缩效率更高.