数字图像处理实验报告(图像编码)

更新时间:2024-03-16 15:02:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

实验三 图像编码

一、实验内容:

用Matlab语言、C语言或C++语言编制图像处理软件,对某幅图像进行时域

和频域的编码压缩。

二、实验目的和意义:

1. 掌握哈夫曼编码、香农-范诺编码、行程编码 2.了解图像压缩国际标准

三、实验原理与主要框架:

3.1实验所用编程环境:

Visual C++6.0(简称VC)

3.2实验处理的对象:256色的BMP(BIT MAP )格式图像

BMP(BIT MAP )位图的文件结构:(如图3.1)

图3.1 位图的文件结构

具体组成图:

第 1 页 共 32 页

BITMAPFILEHEADER 位图文件头 (只用于BMP文件) bfType=”BM” bfSize bfReserved1 bfReserved2 bfOffBits biSize biWidth biHeight biPlanes biBitCount biCompression biSizeImage biXPelsPerMeter biYPelsPerMeter biClrUsed biClrImportant 单色DIB有2个表项 16色DIB有16个表项或更少 256色DIB有256个表项或更少 真彩色DIB没有调色板 每个表项长度为4字节(32位) 像素按照每行每列的顺序排列 每一行的字节数必须是4的整数倍BITMAPINFOHEADER 位图信息头 Palette 调色板 DIB Pixels DIB图像数据

3.3 数字图像基本概念

数字图像是连续图像f(x,y)的一种近似表示,通常用由采样点的值所组成的矩阵来表示:

?f(0,0)?f(1,0)??...??f(N?1,0)f(0,1)... f(1,1)......f(N?1,1)...f(0,M?1)?f(1,M?1)??

?...?f(N?1,M?1)?每一个采样单元叫做一个像素(pixel),上式(2.1)中,M、N分别为数字图像在横(行)、纵(列)方向上的像素总数。在计算机内通常用二维数组来表示数字图像的矩阵,把像素按不同的方式进行组织或存储,就得到不同的图像格式,把图像数据存成文件就得到图像文件。图像文件按其数字图像格式的不同一般具有不同的扩展名。最常见的图像格式是位图格式,其文件名以BMP为扩展名。图像数字

第 2 页 共 32 页

化的精度包括两部分,即分辨率和颜色深度。分辨率指图像数字化的空间精细度,有显示分辨率和图像分辨率两种。

数字图像的颜色深度表示每一像素的颜色值所占的二进制位数。颜色深度越大则能表示的颜色数目越多。颜色深度的不同,就产生不同种类的图像文件,在计算机中常使用图像文件的类型有单色图像、灰度图像、伪彩色图像和24位真彩色图像。它们之间的关系取决于数字图像采用的颜色表示法。常用的颜色表示法有RGB、CMYK、HSL和YUV等。

数字图像的数据容量非常庞大,如以24位真彩色表示像素为640*480的数字图像,所需要的数据量为900KB,所以需要对数字图像进行数据压缩。数据压缩包括数据压缩编码和压缩数据解码两个过程。

图像压缩的基本原则包括:

(1)编码重复压缩:按照编码重复的概率大小做压缩编码,压缩重复概率大的编码,可以节省一些存储空间。

(2)像素间重复压缩:前后像素间存在的某种程度的相关,如存在相同的背景图像等,简化编码以节省空间。

(3)视觉重复压缩:由于入眼的生理构造,就算在像素之间少了几个像素,眼睛也看不出来。但是,只是在某些情况中才可以允许这种失真性较大的应用。

主要的数字图像压缩标准是JPEG(Joint Picture Expert Group)标准。

数字图像的采集设备主要有扫描仪、数字相机和图像采集卡等。通过对数字图像进行一定的处理,即图像处理,可在一定程度上改善图像的分辨质量和形成特殊的视觉效果。数字图像处理由数字图像处理系统完成,其结构主要包括图像采集系统、计算机图像处理系统和图像输出系统三部分。

3.4图像的编码

图像的编码包括图像的离哈夫曼编码、香农-范诺编码、行程编码,基本框架(如图3.2)和图像编码菜单设计图(如图3.3)如下:

第 3 页 共 32 页

图3.2 图像编码流程图

图3.3 bmp图像编码菜单设计图

四、数字图像编码技术:

4.1 哈夫曼编码

根据信息论中信源编码理论,当平均码长R大于等于图像熵H时,总可设计出一种无失真编码。当平均码长大于图像熵时,表明该编码方法效率很低;当平

第 4 页 共 32 页

均码长等于或很接近于(但不大于)图像熵时,称此编码方法为最佳编码,此时不会引起图像失真;当平均码长小于图像熵时,压缩比较高,但会引起图像失真。在变长编码中,如果码字长度严格按照对应符号出现的概率大小逆序排列,但其平均码字长度为最小,这就是变长最佳编码定理。变长最佳编码定理是哈夫曼编码的理论基础。

4.1.1 哈夫曼编码基本原理

哈夫曼(Huffman)编码是一种常用的压缩编码方法,是Huffman于1952年为压缩文本文件建立的,是一种效率比较高的变长无失真信源编码方法。它的基本原理是频繁使用的数据用较短的代码代替,较少使用的数据用较长的代码代替,每个数据的代码各不相同。

由于哈夫曼编码是以信源概率分布为基础的,但一般情况下无法事先知道信源的概率分布,因而通常采用对大量数据进行统计后得到的近似分布来代替,这样会导致实际应用时哈夫曼编码无法达到最佳性能。 4.1.2 哈夫曼编码步骤

下面来介绍哈夫曼编码方法:

(1)将输入的符号(图像中的灰度级)ai按出现概率P(ai)由小到大排列,即P(ai)?P(aj)?P(ak)...

(2)将最小的两个P(ai)相加,形成一个新的概率集合(此时压缩了一个P(ai)),再按(1)重复直到只剩下两个概率为止。

下图给出了一个实际信源符号的缩减过程。

表3.1 哈夫曼编码中的信源符号缩减过程

原始信源 符号 概率 0.02 0.04 0.06 0.12 0.16 0.2 0.4 信源符号缩减步骤 1 2 3 4 5 0.06 0.06 0.12 0.12 0.12 0.16 0.16 0.16 0.2 0.24 0.2 0.2 0.24 0.36 0.4 0.4 0.4 0.4 0.4 0.6 a5 a4 a7 a3 a1 a6 a2

第 5 页 共 32 页

从最后的两概率值开始逐步向前给符号分配码字长,每一步有两个分支,以相同的规则各赋予一个二进制码。编码过程如表3.2所示。

表3.2哈夫曼编码中的码字分配过程 原始信源 符号 概率 0.02 0.04 0.06 0.12 0.16 0.2 0.4 码字 01111 01110 0110 010 001 000 1 0.06 0.06 0.12 0.16 0.2 0.4 0111 0110 010 001 000 1 0.12 0.12 0.16 0.2 0.4 011 010 001 000 1 0.16 0.2 0.24 0.4 001 000 01 1 0.24 01 0.36 00 0.4 1 0.4 0.6 1 0 按信源符号缩减逆向赋码字 1 2 3 4 5 a5 a4 a7 a3 a1 a6 a2 同时也可以计算与编码性能相关的几个参数: (1)信源的熵

H(A)???P(ai)log2P(ai)?2.325(3.1)

i?17

(2)哈夫曼编码的平均码字长

Lavg??l(i?17a)ip(a)i?3?0.1?6?10.?4?30.?12?...?4?0. 0 6 2. 380(3.2)

(3)哈夫曼编码的效率

H(A)2.325??0.97 7 ?? Lavg2.328(3.3)

4.1.3 哈夫曼编码特点

用哈夫曼编码方法压缩图像数据,对于不同的图像,其压缩效果和压缩效率是不同的。当各符号出现概率不同时,编码效率较高。而当各符号出现的概率相等时,实际上此时的哈夫曼编码就已退化成等长编码,编码效率较低。对灰度图像的无损压缩算法采用哈夫曼编码,即对原图像实行直接的编码,该算法在恢复

第 6 页 共 32 页

图像的质量上是极好的。但由于该算法对原图像直接使用不等长编码压缩,但没有处理图像各个像素间的相关性,从而使压缩效率并不是很高,因此必须结合其它的压缩方法同时使用以达到更高的压缩比。

4.2 香农-范诺编码

香农-范诺(Shannon-Fannon)编码也是一种常见的可变字长编码。与哈夫曼编码相似,当信源符号出现时的概率正好为2-i时,采用香农-范诺编码同样能达到100%的编码效果。

4.2.1 香农-范诺编码基本原理

香农-范诺编码的理论基础是符号的码字长度Ni完全由该符号出现的概率来决定,即 -logDPi?Ni?-logDPi+1,式中D为编码所用的数制。 4.2.2 香农-范诺编码步骤

(1)将信源符号Xi按其出现概率Pi从小到大排序:

?x1x2...xn?X1???;P1?P2?...?Pn?P1P1...Pn?(3.3)

(2)将X分成两个子集

?x1x2...xk??xk?1xk?2...xn?X1???X2???P1P1...PkPk?1Pk?2...Pn????(3.4)

并且保证

?P??P成立或差不多成立。在本图像压缩编码设计中是按前

iji?1j?k?1kn一个子集大于等于总和的一半来计算的。

(3)给两个子集赋不同的码元值,如X1中的符号赋“1”,X2中的符号就赋“0”。

(4)重复(2)、(3),即对每个子集再一分为二,并分别赋予不同码元值,直到每个子集仅含一个信源符号为止。

第 7 页 共 32 页

下面通过哈夫曼编码中用过的同样例子,说明上述编码过程。将信源

a4a7a3a1a6a2??a5A???0.020.040.060.120.160.20.4??(3.5)

?x1X???0.x202x30.x404x?x5? ?0.06(3.6)

则其编码过程如表3.3所示。

表3.3香农-范诺编码过程 码字 111111 111110 11110 1110 110 10 0 符号 X1 X2 X3 X4 X5 X6 X7 概率 0.02 0.04 0.06 0.12 0.16 0.2 0.4 0 0 0 1 1 0 1 1 0 1 1 0 4.2.3 香农-范诺编码特点

采用以上算法用VC具体实现对某一图像进行编码,同时对编码的有关评估参数如图像熵值、平均码长、编码效率进行计算。为了便于编码的显示设计了编码显示对话框,在其中进行编码运算并实现码字的显示。

4.3 行程编码

行程编码又称行程长度编码(Run Length Encoding, RLE),是一种熵编码,其编码原理相当简单,即将具有相同值的连续串用其串长和一个代表值来代替,该连续串就称为行程,串长称为行程长度。RLE压缩编码尤其适用于计算机生成的图形图像,对减少存储容量很有效果。 4.3.1行程编码基本原理

行程编码分为定长和不定长编码两种。定长编码是指编码的行程长度所用的

第 8 页 共 32 页

二进制位数固定,而变长形成编码是指对不同范围的行程长度使用不同位数的二进制进行编码。使用变长行程编码需要增加标志位来表明所使用的二进制位数。

为了达到较好的压缩效果,一般不单独采用行程编码,而是和其他编码方法结合使用。例如,在JPEG中,就综合使用了行程编码、DCT、量化编码以及哈夫曼编码,先对图像作分块处理,再对这些分块图像进行离散余弦变换(DCT),对变换后的频域数据进行量化并作Z字形扫描,接着对扫描结果作行程编码,对行程编码后的结果在作哈哈夫曼编码。 4.3.2行程编码步骤

PCX文件分为文件头和图像压缩数据两个部分。如果是256色图像,则还有一个256色调色板存于文件尾部。文件头全长128字节,包含了图像的大小和颜色以及PCX文件的版本标志等信息。其中头信息的具体结构为:

typedef struct{

char manufacturer; char version; char encoding; char bits_per_pixel; WORD xmin,ymin; WORD xmax,ymax; WORD hres; WORD vres; char palette[48]; char reserved;

char colour_planes; WORD bytes_per_line; WORD palette_type;

char filler[58]; }PCXHEAD;

其中值得注意的是以下几个数据:manufacturer为PCX文件的标志,必须为0x0a;xmin为最小的x坐标,xmax为最大的x坐标,所以图像的宽度为xmax-xmin+1,同样图像的高度为ymax-ymin+1;bits_per_pixel为每个编码行所占的字节数。

图像压缩数据紧跟在文件头后。如果没有使用调色板,那么图像压缩数据存储的是实际像素值;否则,存储的是调色板的索引值。当压缩数据是实际的像素值时,它们按颜色平面和扫描行存储,即每行先存储所有R分量,再存储所有G分量,最后存储所有B分量,一行数据存储完后,接着存储下一行数据。如果使用了调色板,则不会分解为单独的颜色平面存储。

第 9 页 共 32 页

下面以256色的PCX文件为例,说明PCX文件中的行程编码。

256色PCX文件中,每个像素占一个字节,压缩数据以字节为单位逐行进行编码,每行填充到偶数字节。PCX文件规定编码时的最大行程长度为63,如果行程长度大于63,则必须分多次存储。对于长度大于1的行程,编码时先存入其行程长度(长度L加上192即0xC0),再存入该行程的代码值,行程长度和行程的代表值分别占一个字节。对于长度为1的行程,即单个像素,如果该像素的值小于或等于0xC0,则编码时直接存入该像素值,而不存储长度信息;否则,先存入0xC1,再存入该像素值,这样做的目的是为了避免该像素值被误认为长度信息。例如,连续100个灰度值为0x80的像素,其编码(以十六进制表示)应为FF 80 25 80。上面的编码中出现的FF的长度信息是由63与0xC0相加所得。 4.3.3行程编码特点

行程编码比较适合于二值图像(即图像的各像素只有两个值——黑或者白)的编码,一般用于量化后出现大量零系数连续的场合,用行程来表示连零码。如果图像是由很多块颜色或灰度相同的大面积区域组成的,那么采用行程编码可以达到很高的压缩比。如果图像中的数据非常分散,则行程编码不但不能压缩数据,反而会增加图像文件的大小。极端情况如果图像中每两个相邻点的颜色都不同,用这种算法不但不能压缩,反而数据量增加一倍。所以现在单纯采用行程编码的压缩算法用得并不多,PCX文件算是其中的一种。

五、实验内容:

5.1 哈夫曼编码

5.1.1 哈夫曼编码算法实现

资源文件HuffmanCoding.cpp中主要介绍哈夫曼编码的算法,首先要从视图类

CDImageProcessView中的OnHuffmancoding函数中得到0-255各灰度值出现的概率,在下面的算法中要基于这个概率值进行哈夫曼编码。

首先要对这个概率值进行从小到大的冒泡排序,然后从概率大于0处开始编码,灰度值较小的编为1,灰度值较大的编为0,再将最小的两个灰度值相加,将加后的值与其他灰度值放在一起重新按从小到大排序,重复以上的步骤直到概率

第 10 页 共 32 页

int j;

graySum = width * height; // 计算图象象素总数 for (i = 0; i < graygrade; i ++) { // 赋零值

grayfreq[i] = 0.0; }

//计算各个灰度值的计数 for (i = 0; i < height; i ++)

{ }

//计算各个灰度值出现的概率

for (i = 0; i < graygrade; i ++) { }

grayfreq[i] = grayfreq[i] / (double)graySum; for (j = 0; j < width; j ++)

{ //指向图象指针 lpSrc = (unsigned char*)lpDIBBits + width * i + j; }

//相应的灰度计数加1

grayfreq[*(lpSrc)] = grayfreq[*(lpSrc)] + 1;

//构建霍夫曼编码的码表,并用对话框显示霍夫曼码表 CHuffmanCoding coding;

// 创建对话框

coding.grayfreq = grayfreq; // 初始化变量值 coding.graygrade = graygrade;

coding.DoModal(); // 显示对话框 EndWaitCursor(); // 恢复光标

5.2 香农-范诺编码

5.2.1 香农-范诺编码算法实现

资源文件ShannonCoding.cpp是香农-范诺编码的算法程序。与哈夫曼算法相似地从视图类中得到每个灰度值出现的概率,针对这个概率进行编码。

首先用冒泡法从小到大对概率进行排序,从概率大于0处开始进行编码,对概率区间进行分割,然后对每个灰度值对应的编码数组进行追加字符,初始时追加为1,当超出概率总和的一半时,追加的字符改为0,接着继续对上半部分的区间进行分割,此时概率的总和就改为上半部分的概率总和了,重复上述操作,完成整幅图像的编码。具体代码如下所示:

BOOL CShannonCoding::OnInitDialog()

第 16 页 共 32 页

{ // 调用默认得OnInitDialog()函数 CDialog::OnInitDialog(); // 字符串变量

CString str; str = \

int i; int j;

//图像灰度出现概率中间结果的数组

double * temp;

//数组用来存放灰度值和其位置之间 int * turn; temp = new double[graygrade];

// 数组用来存放灰度值和其位置之间的映射 turn = new int[graygrade]; //临时变量

int temp2; // 当前编码区间的概率和

double effectcum;

//初始的概率和为1.0 effectcum = 1.0; // 已经编码灰度值概率的统计和 double

dSum;

//初始化为0

dSum = 0;

// 已编码灰度值 int lCount = 0;

// 起始位置

int lBegin;

//指示编码是否已经完成一段 BOOL * bFinished; bFinished = new BOOL[graygrade]; // 分配内存

m_strCode = new CString[graygrade]; for (i = 0; i < graygrade; i ++) { // 初始化为FALSE

bFinished[i] = FALSE; // 将概率赋值temp数组

temp[i] = grayfreq[i];

// 按灰度值大小顺序排列 turn[i] = i; }

// 中间变量

double

te;

第 17 页 共 32 页

// 用冒泡法对进行灰度值出现的概率排序 // 同时改变灰度值位置的映射关系 for (j = 0; j < graygrade - 1; j ++) { for (i = 0; i < graygrade - j - 1; i ++) { if (temp[i] > temp[i + 1]) { // 互换 te = temp[i]; temp[i] = temp[i + 1]; temp[i + 1] = te;

// 将i和i+1灰度的位置值互换 temp2 = turn[i]; turn[i] = turn[i+1]; turn[i+1] = temp2;

}

} }

// 计算香农-弗诺编码表,从概率大于0处开始编码 for (lBegin = 0; lBegin < graygrade - 1; lBegin ++) { if (temp[lBegin] > 0) {

break; }

}

// 开始编码

while(lCount < graygrade)

{ // 从概率大于零的灰度值开始编码 lCount = lBegin; // 对区间进行分割,对每个区间的灰度值编码 for (i = lBegin; i < graygrade; i ++) { // 判断是否编码完成 if (bFinished[i] == FALSE) { // 增加当前编码区间的概率综合 dSum += temp[i]; // 判断是否超出总和的一半 if (dSum > effectcum/2.0) { // 超出,追加的字符改为0 str = \ } // 追加字符

m_strCode[turn[i]] = m_strCode[turn[i]] + str; 第 18 页 共 32 页

// 判断是否编码完一段

if (dSum == effectcum)

{ // 完成一部分编码,重新计算effectcum

dSum = 0; // 初始化dSum为0 // 判断是不是对所有灰度值已经编码一遍 if (i == graygrade - 1) {

j = lBegin; }

else {

j = i + 1; }

temp2 = j; // 保存j值 str = m_strCode[turn[j]];

// 计算下一编码区间的概率总和 effectcum = 0;

for (; j < graygrade; j++)

{ // 判断是否是同一段编码 if ((m_strCode[turn[j]].Right(1) != str.Right(1)) || (m_strCode[turn[j]].GetLength() != str.GetLength())) }

break;

//当前区间的概率总和增加 effectcum += temp[j];

// 码字增加值为1 str = \ // 判断该段编码已经完成 if (temp2 + 1 == j)

{

bFinished[temp2] = TRUE; }

} else

}

{ // 开始下一轮编码 lCount ++; // 重新赋dSum为0 dSum = 0;

// 判断是不是对所有灰度值已经编码一遍 if (i == graygrade - 1)

{

j = lBegin; }

第 19 页 共 32 页

else }

{

j = i + 1;

temp2 = j; // 保存j值 str = m_strCode[turn[j]];

// 计算下一编码区间的概率总和 effectcum = 0;

for (; j < graygrade; j++) {

// 判断是否是同一段编码 if ((m_strCode[turn[j]].Right(1) != str.Right(1)) }

|| (m_strCode[turn[j]].GetLength() != str.GetLength())) { // 退出循环 break; }

effectcum += temp[j]; // 累加

str = \

// 判断该段编码已经完成 if (temp2 + 1 == j) }

bFinished[temp2] = TRUE;

} }

// 计算图像熵

for (i = 0; i < graygrade; i ++) { // 判断概率是否大于0 if (grayfreq[i] > 0)

{ // 计算图像熵 m_shang -= grayfreq[i] * log(grayfreq[i]) / log(2.0);

} }

// 计算平均码字长度

for (i = 0; i < graygrade; i ++)

{ // 累加 m_length +=grayfreq[i] * m_strCode[i].GetLength(); }

// 计算编码效率

m_effect = m_shang / m_length; // 保存变动

UpdateData(FALSE); //输出编码结果

// 字符串变量,列表项目的显示

第 20 页 共 32 页

CString listname;

// 控件ListCtrl的ITEM LV_ITEM codingItem; // 保存控件ListCtrl中添加的ITEM编号 int nItem2View;

// 设置CListCtrl控件样式

m_codinglist.ModifyStyle(LVS_TYPEMASK, LVS_REPORT); // 给List控件添加Header

m_codinglist.InsertColumn(0, \像素灰度\ m_codinglist.InsertColumn(1, \灰度概率\ m_codinglist.InsertColumn(2, \编码码字\ m_codinglist.InsertColumn(3, \码字长度\ // 设置样式为文本

codingItem.mask = LVIF_TEXT;

// 添加显示

for (i = 0; i < graygrade; i ++) { // 第一列显示 codingItem.iItem = m_codinglist.GetItemCount(); listname.Format(\ codingItem.iSubItem = 0; //显示像素灰度

codingItem.pszText= (LPTSTR)(LPCTSTR)listname; nItem2View = m_codinglist.InsertItem(&codingItem); codingItem.iItem = nItem2View; // 第二列显示

codingItem.iSubItem = 1; listname.Format(\

codingItem.pszText = (LPTSTR)(LPCTSTR)listname; m_codinglist.SetItem(&codingItem); // 第三列显示

codingItem.iSubItem = 2;

codingItem.pszText = (LPTSTR)(LPCTSTR)m_strCode[i]; m_codinglist.SetItem(&codingItem); // 第四列显示

codingItem.iSubItem = 3;

listname.Format(\ codingItem.pszText = (LPTSTR)(LPCTSTR)listname; m_codinglist.SetItem(&codingItem); }

delete turn; //内存释放 delete temp;

delete bFinished;

return TRUE;

第 21 页 共 32 页

}

5.2.2 视图类处理函数On Shannon Coding()函数

关于函数的调用部分,在图像处理的主窗口的菜单栏“图像编码”下添加“香农-范诺编码”项,对应的处理函数是CDImageProcessView视图类中的OnShannoncoding()函数。OnShannoncoding()函数实现了图像灰度概率的计算和编码对话框的调用,具体代码如下:

void CDImageProcessView::OnShannoncoding() { // 获取文档

CDImageProcessDoc* pDoc = GetDocument(); //指向源图像的指针 unsigned char* lpSrc;

// 指向DIB的指针 LPSTR lpDIB; // 指向DIB象素指针

LPSTR lpDIBBits; //图像灰度等级数

int graygrade;

// 锁定DIB

lpDIB = (LPSTR) ::GlobalLock((HGLOBAL) pDoc->GetHObject()); // 找到DIB图像象素起始位置

lpDIBBits = pDoc->m_dib.GetBits(lpDIB); // 判断是否是8位位图

if (pDoc->m_dib.GetColorNum(lpDIB) != 256)

{ // 提示用户 MessageBox(\目前只支持256色位图!\系统提示\ }

MB_ICONINFORMATION | MB_OK);

::GlobalUnlock((HGLOBAL) pDoc->GetHObject()); // 解除锁定 return; // 返回

//灰度等级数为256

graygrade=pDoc->m_dib.GetColorNum(lpDIB); //各灰度等级出现的概率

double * grayfreq;

//图像宽度

int width; //图像高度

int height;

width=pDoc->m_dib.GetWidth(lpDIB); height=pDoc->m_dib.GetHeight(lpDIB); // 更改光标形状

BeginWaitCursor();

第 22 页 共 32 页

//开始计算各个灰度级出现的概率,如果需要对指定的序列进行哈夫曼编码, //只要将这一步改成给各个灰度级概率赋值即可

grayfreq = new double[graygrade]; // 分配内存 int graySum; //像素数目 int i; //循环变量 int j; //循环变量

// 计算图象象素总数

graySum = width * height;

{ // 赋零值 grayfreq[i] = 0.0; }

for (i = 0; i < graygrade; i ++)

// 计算各个灰度值的计数

for (i = 0; i < height; i ++) {

for (j = 0; j < width; j ++)

{ // 指向图象指针 lpSrc = (unsigned char*)lpDIBBits + width * i + j; // 相应的灰度计数加1 grayfreq[*(lpSrc)] = grayfreq[*(lpSrc)] + 1; }

}

for (i = 0; i < graygrade; i ++) { // 计算各个灰度值出现的概率 grayfreq[i] = grayfreq[i] / (double)graySum; }

CShannonCoding coding; // 创建对话框 coding.grayfreq = grayfreq; // 初始化变量值 coding.graygrade = graygrade;

coding.DoModal(); // 显示对话框 EndWaitCursor(); // 恢复光标 }

5.3 行程编码

5.3.1 行程编码算法实现

函数PCXCoding()将指定的256色DIB对象保存为256色PCX文件。参数LPSTR lpDIBBits是指向DIB对象像素的指针; 参数LONG ImageWidth 是源图像宽度(像素数,必须是4的倍数);参数LONG ImageHeight是源图像高度(像素数);参数CFile& file是要保存的文件。

BOOL PCXCoding(LPSTR lpDIBBits,LONG ImageWidth,LONG ImageHeight,CFile& file)

第 23 页 共 32 页

{ typedef struct{ char manufacturer; char version; char encoding; char bits_per_pixel; WORD xmin,ymin; WORD xmax,ymax; WORD hres; WORD vres; char palette[48]; char reserved; char colour_planes; WORD bytes_per_line; WORD palette_type;

char filler[58];

}PCXHEAD;

int i; int j; //中间变量

BYTE bChar1; //中间变量

BYTE bChar2; //指向源图像像素的指针

BYTE * lpSrc; //指向编码后图像数据的指针

BYTE * lpDst; //重复像素计数

int iCount; //缓冲区已使用的字节数

DWORD dwBuffUsed; //PCX文件头

PCXHEAD pcxHeadr; //PCX标志码

pcxHeadr.manufacturer= 0x0A; //PCX版本号

pcxHeadr.version =5; //PCX编码方式(1表示RLE编码)

pcxHeadr.encoding =1; //像素位数(256色为8位)

pcxHeadr.bits_per_pixel =8; //图像相对于屏幕的左上角X坐标

pcxHeadr.xmin=0; //图像相对于屏幕的左上角Y坐标

第 24 页 共 32 页

pcxHeadr.ymin=0; //图像相对于屏幕的右下角X坐标

pcxHeadr.xmax=ImageWidth-1; //图像相对于屏幕的右下角Y坐标

pcxHeadr.ymax=ImageHeight-1; //图像的水平分辨率

pcxHeadr.hres = ImageWidth; //图像的垂直分辨率

pcxHeadr.vres = ImageHeight; for(i = 0 ; i < 48 ; i++)

{ //调色板数据(对于256色PCX无意义,直接赋值为0) pcxHeadr.palette[i] = 0; }

//保留域 设定为0

pcxHeadr.reserved =0; //图像色彩平面数目对于256色PCX设为1 pcxHeadr.colour_planes=1; //图像的宽度(字节为单位)必须为偶数 pcxHeadr.bytes_per_line = ImageWidth;

//图像调色板的类型,1表示彩色或者单色图像,2表示图像是灰度图 pcxHeadr.palette_type = 1; for(i = 0; i < 54; i ++) {

pcxHeadr.filler[i]= 0; }

//写入头文件

file.Write((LPSTR)&pcxHeadr,sizeof(PCXHEAD)); //分配内存以保存编码结果

lpDst = new BYTE[ImageHeight * ImageWidth*2]; //指明当前已经用了多少缓冲区(字节数) dwBuffUsed = 0; for(i=0;i

{ //指向DIB第i行,第0个像素的指针 lpSrc = (BYTE *)lpDIBBits + ImageWidth *(ImageHeight -1 -i);

//给bChar1赋值

bChar1 = *lpSrc; //设置iCount为1

iCount = 1; for(j =1; j< ImageWidth; j++) { lpSrc++; bChar2 = *lpSrc;

if((bChar1==bChar2)&&(iCount<63))

iCount++;

第 25 页 共 32 页

else

{ if((iCount>1)||(bChar1>= 0xC0)) { lpDst[dwBuffUsed] = iCount | 0xC0; lpDst[dwBuffUsed+1] = bChar1;

dwBuffUsed += 2;

} else { lpDst[dwBuffUsed] = bChar1;

dwBuffUsed++;

}

bChar1 = bChar2;

iCount =1;

}

}

if((iCount > 1) ||(bChar1>=0xC0)) { lpDst[dwBuffUsed] = iCount | 0xC0;

lpDst[dwBuffUsed+1]= bChar1;

dwBuffUsed+=2; } else { lpDst[dwBuffUsed] = bChar1;

dwBuffUsed++;

}

}

file.WriteHuge((LPSTR)lpDst,dwBuffUsed); delete[] lpDst;

lpDst = new BYTE[769]; * lpDst = 0x0C; for(i=0;i<256;i++) {

lpDst[i*3+1] =i; lpDst[i*3+2] =i; lpDst[i*3+3] =i; }

file.Write((LPSTR)lpDst,769); return TRUE; }

5.3.2 视图类处理函数OnRleCoding()

第 26 页 共 32 页

关于 PCXCoding的函数调用部分,在图像处理的主窗口菜单栏“图像编码”下添加“行程编码”项,对应的处理函数是CDImageProcessView视图类中的OnRleCoding()函数,具体代码如下。

void CDImageProcessView::OnPcxcoding()

{

CDImageProcessDoc* pDoc = GetDocument(); LPSTR lpDIB; LPSTR lpDIBBits;

lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)pDoc->GetHObject()); lpDIBBits = pDoc->m_dib.GetBits(lpDIB); if(pDoc->m_dib.GetColorNum(lpDIB)!=256) {

MessageBox(\目前只支持256色位图!\系统提示\TION| MB_OK); ::GlobalUnlock((HGLOBAL)pDoc->GetHObject());

return; } int width; int height;

width= pDoc->m_dib.GetWidth(lpDIB); height= pDoc->m_dib.GetHeight(lpDIB); BeginWaitCursor(); CString strFilePath;

strFilePath = pDoc->GetPathName();

if(strFilePath.Right(4).CompareNoCase(\

{ strFilePath=strFilePath.Left(strFilePath.GetLength()-3)+\ } else { }

CfileDialog

dlg(FALSE,\Y|OFN_OVERWRITEPROMPT,\X图像文件(*.PCX) |*.PCX|所有文件(*.*)|*.*||\ if(dlg.DoModal()!=IDOK) {

strFilePath +=\

::GlobalUnlock((HGLOBAL)pDoc->GetHObject()); EndWaitCursor();

return; }

strFilePath = dlg.GetPathName(); CFile file; CFileException fe;

第 27 页 共 32 页

if(!file.Open(strFilePath,CFile::modeCreate|CFile::modeReadWrite|CFile::shareExclusive,&fe)) {

MessageBox(\打开指定PCX文件时失败!\系统提示\TION|MB_OK); return;

}

if(PCXCoding(lpDIBBits,width,height,file)) { } else {

MessageBox(\成功保存为PCX文件!\系统提示\

MessageBox(\保存为PCX文件失败!\系统提示\TION|MB_OK); }

::GlobalUnlock((HGLOBAL)pDoc->GetHObject()); EndWaitCursor();

六、实验结果:

如下图6.1所示,分别采用哈夫曼编码、香农-范诺编码和行程编码进行图像压缩处理,对比其压缩后的数据来分析编码的优缺点。

图6.1位图图像“原始lena图像”

6.1 哈弗曼编码结果

对位图图像“原始lena图像”进行哈夫曼编码压缩,如下图6.2所示。编码效率约为99.7%,图像熵值为7.44,平均码长7.47。

第 28 页 共 32 页

图6.2哈夫曼编码运行结果

6.2 香农-范诺编码结果

对位图图像“原始lena图像”进行香农-范诺编码压缩,如下图6.3所示。编码效率约为99.2%,平均码长为7.5,图像熵值7.44。

图6.3香农-范诺编码运行结果

6.3 行程编码结果

第 29 页 共 32 页

行程编码是将位图图像,即以.bmp为后缀的图像保存为以.PCX为后缀的文件格式

图6.4保存为以.PCX为后缀的文件

若保存成功,则弹出对话框提示成功保存,如图6.5所示。

图6.5成功保存提示对话框

保存成PCX文件后,其压缩程度可从属性中看出来,如图6.6所示。

第 30 页 共 32 页

图6.6行程编码后压缩程度对比

七、总结与体会:

通过对哈夫曼编码、香农-范诺编码和行程编码的研究及系统测试,得出如下结论:

(1)哈夫曼编码有较高的编码效率,对不同概率分布的信源,哈夫曼编码的编码效率有所差别。根据信息论中信源编码理论,对于二进制编码,当信源概率为2的负幂次方时,哈夫曼编码的编码效率可达到100%,其平均码字长度也很短;而当信源概率为均匀分布时,其编码效果明显降低。

(2)香农-范诺编码虽属于概率匹配范畴,但并未严格遵守匹配规则,即不全是按“概率大码长小、概率小码长大”来决定码长,有时会出现概率小码长反而小的情况,因此平均码长一般不会最小。此种编码方法也有较高的编码效率,比较适合于每次分组概率都很接近的信源,特别是对每次分组概率都相等的信源进行编码时,可达到理想的编码效率。

(3)行程编码比较适合于二值图像(即图像的各像素只有两个值--黑或者白)的编码,一般用于量化后出现大量零系数连续的场合,用行程来表示连零码。如果图像是由很多块颜色或灰度相同的大面积区域组成的,那么采用行程编码可以

第 31 页 共 32 页

达到很高的压缩比。如果图像中的数据非常分散,则行程编码不但不能压缩数据,反而会增加图像文件的大小。极端情况如果图像中每两个相邻点的颜色都不同,用这种算法不但不能压缩,反而数据量增加一倍。

总之,每种编码方法都有各自的优缺点,现实中的压缩图像一般都是几种方法的结合使用,以达到最佳效果。

第 32 页 共 32 页

本文来源:https://www.bwwdw.com/article/xci8.html

Top