目录
实验目的
掌握DPCM编解码系统的基本原理。初步掌握实验用C/C++/Python等语言编程实现DPCM编码器,并分析其压缩效率。
实验原理
DPCM编解码原理
DPCM是差分预测编码调制的缩写,是比较典型的预测编码系统。在DPCM系统中,需要注意的是预测器的输入是已经解码以后的样本。之所以不用原始样本来做预测,是因为在解码端无法得到原始样本,只能得到存在误差的样本。因此,在DPCM编码器中实际内嵌了一个解码器,如编码器中虚线框中所示。在一个DPCM系统中,有两个因素需要设计:预测器和量化器。理想情况下,预测器和量化器应进行联合优化。实际中,采用一种次优的设计方法:分别进行线性预测器和量化器的优化设计。在这种方法中,量化电平数必须足够大(M>=8)才能获得很好的性能。
信噪比:
其中:为量化误差的均方值,
为预测误差的均方值。
预测误差为:
预测误差的方差为:
若用MSE(
均方误差)作为失真度量,这最小化失真的最佳预测器为:
(若采用前N个样本预测。且每个样本为B比特,则上述条件概率需要有一个项的表。)
线性预测器的设计
设计目标:具有最小均方误差的预测器-最大的预测增益,即在最小的条件下确定一组最佳预测系数(略去量化误差)。
图像客观质量评价
均方误差
e(t)为 原始波形与重建波形直插,
为误差均值通常为零。
使均方误差MSE最小的编码器设计方法称为最小均方误差(MMSE)设计。(
,
是取样后的信号)最为信号质量的客观评判标准和MMSE的设计准则。
信噪比SNR
(信号方差与重建误差方差的比值)
由于按照离散图像场计算简便,SNR公式也可以写作:
峰值信噪比PSNR
因为图像幅度的均值通常为非零整数,因而将SNR式子中,用x(m,n)的最大值来代替均方根值,得到峰值信噪比
。
Huffman编码
Huffman是一种无失真信源编码算法,主要目的是根据使用频率来最大化节省字符(编码)的存储空间,编码后可以得到即时的最佳码。
[具体的实现算法]
- 统计符号的发生概率
- 把频率暗从小到大的顺序排列
- 每一次选出最小的两个值,作为二叉树的两个叶子节点,将和作为它们的根节点,这两个叶子节点不再参与比较,新的根节点参与比较。
- 重复3,直到最后得到和为1的根节点。
- 将形成的二叉树的左节点标0,右节点标1,把从最上面的根节点到最下面的叶子节点途中遇到的0,1序列串起来,就得到了各个符号的编码。
产生霍夫曼编码需要对原始数据扫描两遍,第一遍扫描要精确地统计出原始数据中,每个值出现的频率,第二遍是建立霍夫曼树并进行编码,由于需要建立二叉树并遍历二叉树生成编码,因此数据压缩和还原速度都较慢,但简单有效,因而得到广泛的应用。
代码
dpcm.h
#pragma once
void frequency(int num, unsigned char* y, double* fre);
void DPCM(int H, int W, unsigned char* y_in, unsigned char* diff_in, unsigned char* re_in, int Nbit);
DPCM.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#define u_int8_t unsigned __int8 //1个字节
#define u_int unsigned __int32 //4个字节
#define u_int32_t unsigned __int32
#define FALSE false
#define TRUE true
/**********定义bcpm变量及缓存***************/
FILE* qFile = NULL;//输出的预测误差图
FILE* dFile = NULL;//输出的重建图
char* qFileName = NULL;//预测误差图文件名
char* dFileName = NULL;//重建图文件名
float qn;//差值
u_int8_t* qBuf = NULL;//预测误差
u_int8_t* dBuf = NULL;//解码值
void DPCM(int H, int W, unsigned char* y_in, unsigned char* diff_in, unsigned char* re_in, int Nbit)
{
int i, j;
unsigned char* y, * re;
char* diff;
y = (unsigned char*)y_in;//原始图像
diff = (char*)diff_in;//误差
re = (unsigned char*)re_in;//预测图像
int N = pow(2, Nbit);//量化电平数
int step = 512 / N;//量化间隔
int c;
int iq, q;
for (i = 0; i < H; i++)
{
for (j = 0; j < W; j++)
{
//第一列像素
if (j == 0)
{
c = y[i * W + j] - 128;
diff[i * W + j] = c + 128;
q = (int)((c + 255) / step + 0.5);//量化
iq = (int)(q * step + 0.5 - 255);//反量化
re[i * W + j] = iq + 128;//重建电平
}
else
{
c = y[i * W + j] - re[i * W + j - 1];
diff[i * W + j] = c + 128;
q = (int)((c + 255) / step + 0.5);//量化
iq = (int)(q * step + 0.5 - 255);//反量化
re[i * W + j] = iq + re[i * W + j - 1];//重建电平
}
//限制重建电平范围
if (re[i * W + j] > 255)
re[i * W + j] = 255;
if (re[i * W + j] < 0)
re[i * W + j] = 0;
}
}
}
frequence.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
#include<malloc.h>
void frequency(int num, unsigned char* y, double* fre)
{
for (int i = 0; i < 256; i++)
{
fre[i] = 0;
for (int j = 0; j < num; j++)
{
if (y[j] == i)
fre[i] += 1;
}
fre[i] = fre[i] / num;
}
}
main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
#include<malloc.h>
#include"dpcm.h"
int Width = 768;
int Height = 512;
int N = 2; //量化比特数
int main(int argc, char** argv)
{
FILE* srcFile;
FILE* errFile;
FILE* recFile;
FILE* fre1File;
FILE* fre2File;
FILE* fre3File;
char* srcFileName = NULL;
char* errFileName = NULL;
char* recFileName = NULL;
char* fre1FileName = NULL;
char* fre2FileName = NULL;
char* fre3FileName = NULL;
srcFileName = argv[1]; //原图yuv
errFileName = argv[2]; //预测误差图yuv
recFileName = argv[3]; //重建图yuv
fre1FileName = argv[4]; //原始图像频率分布txt
fre2FileName = argv[5]; //重建图像频率分布txt
fre3FileName = argv[6]; //预测误差图像频率分布txt
unsigned char* y, * u, * v, * re, * err;
double* fre1, * fre2, * fre3;
double MSE = 0, PSNR = 0;
//分配缓冲区
fre1 = (double*)malloc(sizeof(double) * 256);
fre2 = (double*)malloc(sizeof(double) * 256);
fre3 = (double*)malloc(sizeof(double) * 256);
y = (unsigned char*)malloc(sizeof(char) * (Width * Height));
u = (unsigned char*)malloc(sizeof(char) * (Width * Height) / 4);
v = (unsigned char*)malloc(sizeof(char) * (Width * Height) / 4);
re = (unsigned char*)malloc(sizeof(char) * (Width * Height));
err = (unsigned char*)malloc(sizeof(char) * (Width * Height));
//打开文件
fopen_s(&srcFile, srcFileName, "rb");
fopen_s(&errFile, errFileName, "wb");
fopen_s(&recFile, recFileName, "wb");
fopen_s(&fre1File, fre1FileName, "wb");
fopen_s(&fre2File, fre2FileName, "wb");
fopen_s(&fre3File, fre3FileName, "wb");
fread(y, 1, Width * Height, srcFile);
fread(u, 1, Width * Height / 4, srcFile);
fread(v, 1, Width * Height / 4, srcFile);
//调用DPCM
DPCM(Height, Width, y, err, re, N);
//计算频率
frequency(Width * Height, y, fre1);
frequency(Width * Height, re, fre2);
frequency(Width * Height, err, fre3);
//计算SME和PSNR
int i;
for (i = 0; i < Width * Height; i++)
{
MSE = MSE + pow((y[i] - re[i]), 2);
}
MSE = MSE / (Width * Height);
PSNR = 10 * log10((255 * 255) / MSE);
printf("N: %d\n", N);
printf("MSE is %lf\n", MSE);
printf("PSNR is %lf\n", PSNR);
//写入重建图像
fwrite(re, 1, (Width * Height), recFile);
fwrite(u, 1, (Width * Height) / 4, recFile);
fwrite(v, 1, (Width * Height) / 4, recFile);
//写入预测误差图像
fwrite(err, 1, (Width * Height), errFile);
fwrite(u, 1, (Width * Height) / 4, errFile);
fwrite(v, 1, (Width * Height) / 4, errFile);
//输出原始图像频率分布
fprintf(fre1File, "value frequency\n");
for (i = 0; i < 256; i++)
{
fprintf(fre1File, "%lf\n", fre1[i]);
}
//输出重建图像频率分布
fprintf(fre2File, "value frequency\n");
for (i = 0; i < 256; i++)
{
fprintf(fre2File, "%lf\n", fre2[i]);
}
//输出预测误差图像频率分布
fprintf(fre3File, "value frequency\n");
for (i = 0; i < 256; i++)
{
fprintf(fre3File, "%lf\n", fre3[i]);
}
fclose(srcFile);
fclose(errFile);
fclose(recFile);
fclose(fre1File);
fclose(fre2File);
fclose(fre3File);
free(y);
free(u);
free(v);
free(err);
free(re);
return 0;
}
设置属性页
实验结果
原图
原图像频率分布图
8bit | 4bit | 2bit | |
MSE | 0.434489 | 322.919492 | 6933.211016 |
PSNR | 51.751010 | 23.039861 | 9.721459 |
重建图 | ![]() | ![]() | ![]() |
预测误差图 | ![]() | ![]() | ![]() |
重建图频率分布 | ![]() | ![]() | ![]() |
预测误差频率分布 | ![]() | ![]() | ![]() |
Huffman编码
使用Huffman编码器将预测误差图像输入,生成huff文件
编码方式 | 压缩比 |
仅熵编码 | 576/523=1.101 |
8bit+熵编码 | 576/382=1.508 |
4bit+熵编码 | 576/452=1.274 |
2bit+熵编码 | 576/536=1.075 |
总结
可见,8bit量化下,经过DPCM+熵编码后的图像压缩比最大,压缩效率最高。其次是4bit>熵编码>2bit;
压缩质量8bit>4bit>2bit