霍夫曼编码实验
霍夫曼(Huffman)编码是经典的无损编码方式,在实际应用中, Huffman编码通常与其他编码技术一起使用。本周根据《数据压缩导论(第四版)》的课后代码进行霍夫曼编码实验。
实验所用到的Sena 、Sensin、Omaha原图像均为256x256像素大小的8位灰度图:
根据 Read.me 文件的引导,代码的运行用到项目的命令参数,其中argc,argv参数的使用方法如下:
void main(int argc, char **argv)
/*argc是命令行总的参数个数;
argv[]是指针数组,共argc个参数,其中第0个参数是程序的全名,以后的参数。命令行后面跟的用户输入的参数。
argv[0]指向程序运行的全路径名
argv[1]指向在DOS命令行中执行程序名后的第一个字符串
argv[2]指向在DOS命令行中执行程序名后的第二个字符串,以此类推
*/
输出一下huff_enc.c 中 usage() 函数的内容,也可以看到具体的命令行输入格式:
Usage:
huff_enc [-i infile][-o outfile][-c codefile][-s storecode][-h]
imagein : file containing the input to be encoded. If no
name is provided input is read from standard in. This
feature can be used to directly encode the output of programs
such as jpegll_enc, and aqfimg_enc.
outfile : File to contain the encoded representation. If no
name is provided the output is written to standard out.
codefile: If this option is used the program will read the
Huffman code from codefile. If the option is not used the
program computes the Huffman code for the file being encoded
storecod: If this option is specified the Huffman code used to
encode the file is stored in codefile. If this option is
not specified the code is stored in outfile
1、对原图像进行Huffman编码
在visual studio 中将编码器项目设为启动项目,以及本地windows调试器的调试属性中,输入项目的命令参数,得到Huffman编码后的文件:
-i ..\images\sena.img -o ..\huffmanout\sena.huff
比较编码前与编码后的文件大小:
图像名称 | Huffman编码前 | Huffman编码后 | 压缩率(%) |
---|---|---|---|
Sena | 64kB | 57kB | 112.28 |
Sensin | 64kB | 60kB | 106.67 |
Omaha | 64kB | 58kB | 110.34 |
可以看到,在使用Huffman编码对图像进行压缩时,压缩率在110%左右,并不是很高,这也是无损压缩的特性带来的结果
2、对残差图像进行Huffman编码
自行编写代码 imgdiff.cpp 生成每个图像所对应的残差图像。残差图像的生成采用后向的DPCM,并采用8bit量化的形式,以sensin为例,残差图像如下:
所用到的DPCM代码:
imgdiff.cpp
/* Your codes here */
#include<stdio.h>
#include<math.h>
#include<iostream>
using namespace std;
#pragma warning(disable:4996);
void dpcm_diff(unsigned char* imagebuf, unsigned char* diffbuf,int width, int height, int bitdepth)
{
for (int i = 0; i < width; i++)
{
for (int j = 1; j < height; j++)
{
diffbuf[i * width + j] = (imagebuf[i * width + j] - imagebuf[i * width + j - 1]+255) / pow(2, (9 - bitdepth));
}
}
for (int i = 0; i < width * height; i++)
{
if (diffbuf[i] > 255)
diffbuf[i] = 255;
if (diffbuf[i] < 0)
diffbuf[i] = 0;
}
}
void main()
{
FILE* image = NULL; FILE* qyuv = NULL; FILE* diff_image = NULL;
const int width = 256; const int height = 256;
int bitdepth = 8;
if (!(image = fopen("../images/sensin.img", "rb")))
{
printf("open file error!");
exit(-1);
}
else
{
printf("open file success");
}
if (!(diff_image = fopen("../out/sensin_diff.img", "wb")))
{
printf("open file error!");
exit(-1);
}
else
{
printf("open file success");
}
unsigned char* imagebuf = (unsigned char*)malloc(sizeof(unsigned char) * width * height);
unsigned char* diffbuf = (unsigned char*)malloc(sizeof(unsigned char) * width * height);
fread(imagebuf, sizeof(unsigned char), width * height, image);
dpcm_diff(imagebuf, diffbuf,width, height, bitdepth);
fwrite(diffbuf, sizeof(unsigned char), width * height, diff_image);
free(diffbuf); free(imagebuf);
fclose(image);
fclose(diff_image);
}
接着用同样的方式对残差图像进行Huffman压缩编码,比较编码前与编码后的文件大小:
图像名称 | Huffman编码前 | Huffman编码后 | 压缩率(%) |
---|---|---|---|
Sena.diff | 64kB | 26kB | 246.15 |
Sensin.diff | 64kB | 30kB | 213.33 |
Omaha.diff | 64kB | 45kB | 142.22 |
可见结合了DPCM的Huffman编码与单独使用Huffman编码相比,压缩率大幅度提升了,DPCM将原图像的分布转化为拉普拉斯分布,对Huffman编码有着良好的适应性。
3、用Sensin图像的码本对其他图像编码
根据命令参数指引,生成Sensin图像的码本:
-i ..\images\sensin.img -o ..\huffmanout\sensin.huff -s .\huffmanout\sensin.code
接着对bookshelf1.img进行编码,使用刚刚生成的码本:
-i ..\images\bookshelf1.img -o ..\huffmanoutout\bookshelf1.huff -c ..\out\sensin.code
比对分析两幅图像的压缩效果:
图像名称 | 编码前 | 编码后(Sensin码本) | 压缩率(%) |
---|---|---|---|
Sensin | 64kB | 57kB | 112.28 |
bookshelf1 | 64kB | 71kB | 90.14 |
可见对于自身的码本,图像进行Huffman编码能够对图像进行压缩,而对于其他图像的码本,在进行Huffman编码时可能不能达到良好的压缩效果,甚至会出现负压缩,这是因为两幅图像的灰度概率分布不一致所造成的。