《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx

上传人:b****6 文档编号:17596223 上传时间:2022-12-07 格式:DOCX 页数:22 大小:130.72KB
下载 相关 举报
《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx_第1页
第1页 / 共22页
《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx_第2页
第2页 / 共22页
《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx_第3页
第3页 / 共22页
《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx_第4页
第4页 / 共22页
《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx

《《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx(22页珍藏版)》请在冰豆网上搜索。

《数字图象处理入门》在线中文教程chp9Word格式文档下载.docx

(3)变换编码;

(4)其它方法。

所谓象素编码是指,编码时对每个象素单独处理,不考虑象素之间的相关性。

在象素编码中常用的几种方法有:

(1)脉冲编码调制(PulseCodeModulation,简称PCM);

(2)熵编码(EntropyCoding);

(3)行程编码(RunLengthCoding);

(4)位平面编码(BitPlaneCoding)。

其中我们要介绍的是熵编码中的哈夫曼(Huffman)编码和行程编码(以读取.PCX文件为例)。

所谓预测编码是指,去除相邻象素之间的相关性和冗余性,只对新的信息进行编码。

举个简单的例子,因为象素的灰度是连续的,所以在一片区域中,相邻象素之间灰度值的差别可能很小。

如果我们只记录第一个象素的灰度,其它象素的灰度都用它与前一个象素灰度之差来表示,就能起到压缩的目的。

如248,2,1,0,1,3,实际上这6个象素的灰度是248,250,251,251,252,255。

表示250需要8个比特,而表示2只需要两个比特,这样就实现了压缩。

常用的预测编码有Δ调制(DeltaModulation,简称DM);

微分预测编码(DifferentialPulseCodeModulation,DPCM),具体的细节在此就不详述了。

所谓变换编码是指,将给定的图象变换到另一个数据域(如频域)上,使得大量的信息能用较少的数据来表示,从而达到压缩的目的。

变换编码有很多,如

(1)离散傅立叶变换(DiscreteFourierTransform,简称DFT);

(2)离散余弦变换(DiscreteCosineTransform,简称DCT);

(3)离散哈达玛变换(DiscreteHadamardTransform,简称DHT)。

其它的编码方法也有很多,如混合编码(HybirdCoding)、矢量量化(VectorQuantize,VQ)、LZW算法。

在这里,我们只介绍LZW算法的大体思想。

值得注意的是,近些年来出现了很多新的压缩编码方法,如使用人工神经元网络(ArtificialNeuralNetwork,简称ANN)的压缩编码算法、分形(Fractl)、小波(Wavelet)、基于对象(ObjectBased)的压缩编码算法、基于模型(Model–Based)的压缩编码算法(应用在MPEG4及未来的视频压缩编码标准中)。

这些都超出了本书的范围。

本章的最后,我们将以JPEG压缩编码标准为例,看看上面的几种编码方法在实际的压缩编码中是怎样应用的。

9.1哈夫曼编码

哈夫曼(Huffman)编码是一种常用的压缩编码方法,是Huffman于1952年为压缩文本文件建立的。

它的基本原理是频繁使用的数据用较短的代码代替,较少使用的数据用较长的代码代替,每个数据的代码各不相同。

这些代码都是二进制码,且码的长度是可变的。

举个例子:

假设一个文件中出现了8种符号S0,S1,S2,S3,S4,S5,S6,S7,那么每种符号要编码,至少需要3比特。

假设编码成000,001,010,011,100,101,110,111(称做码字)。

那么符号序列S0S1S7S0S1S6S2S2S3S4S5S0S0S1编码后变成000001111000001110010010011100101000000001,共用了42比特。

我们发现S0,S1,S2这三个符号出现的频率比较大,其它符号出现的频率比较小,如果我们采用一种编码方案使得S0,S1,S2的码字短,其它符号的码字长,这样就能够减少占用的比特数。

例如,我们采用这样的编码方案:

S0到S7的码字分别01,11,101,0000,0001,0010,0011,100,那么上述符号序列变成011110001110011101101000000010010010111,共用了39比特,尽管有些码字如S3,S4,S5,S6变长了(由3位变成4位),但使用频繁的几个码字如S0,S1变短了,所以实现了压缩。

上述的编码是如何得到的呢?

随意乱写是不行的。

编码必须保证不能出现一个码字和另一个的前几位相同的情况,比如说,如果S0的码字为01,S2的码字为011,那么当序列中出现011时,你不知道是S0的码字后面跟了个1,还是完整的一个S2的码字。

我们给出的编码能够保证这一点。

下面给出具体的Huffman编码算法。

(1) 

 

首先统计出每个符号出现的频率,上例S0到S7的出现频率分别为4/14,3/14,2/14,1/14,1/14,1/14,1/14,1/14。

(2) 

从左到右把上述频率按从小到大的顺序排列。

(3) 

每一次选出最小的两个值,作为二叉树的两个叶子节点,将和作为它们的根节点,这两个叶子节点不再参与比较,新的根节点参与比较。

(4) 

重复(3),直到最后得到和为1的根节点。

(5) 

将形成的二叉树的左节点标0,右节点标1。

把从最上面的根节点到最下面的叶子节点途中遇到的0,1序列串起来,就得到了各个符号的编码。

上面的例子用Huffman编码的过程如图9.1所示,其中圆圈中的数字是新节点产生的顺序。

可见,我们上面给出的编码就是这么得到的。

图9.1 

Huffman编码的示意图

产生Huffman编码需要对原始数据扫描两遍。

第一遍扫描要精确地统计出原始数据中,每个值出现的频率,第二遍是建立Huffman树并进行编码。

由于需要建立二叉树并遍历二叉树生成编码,因此数据压缩和还原速度都较慢,但简单有效,因而得到广泛的应用。

源程序就不给出了,有兴趣的读者可以自己实现。

9.2行程编码

行程编码(RunLengthCoding)的原理也很简单:

将一行中颜色值相同的相邻象素用一个计数值和该颜色值来代替。

例如aaabccccccddeee可以表示为3a1b6c2d3e。

如果一幅图象是由很多块颜色相同的大面积区域组成,那么采用行程编码的压缩效率是惊人的。

然而,该算法也导致了一个致命弱点,如果图象中每两个相邻点的颜色都不同,用这种算法不但不能压缩,反而数据量增加一倍。

所以现在单纯采用行程编码的压缩算法用得并不多,PCX文件算是其中的一种。

PCX文件最早是PCPaintbrush软件所采用的一种文件格式,由于压缩比不高,现在用的并不是很多了。

它也是由头信息、调色板、实际的图象数据三个部分组成。

其中头信息的结构为:

typedefstruct{

 

charmanufacturer;

charversion;

charencoding;

charbits_per_pixel;

WORDxmin,ymin;

WORDxmax,ymax;

WORDhres;

WORDvres;

charpalette[48];

charreserved;

charcolour_planes;

WORDbytes_per_line;

WORDpalette_type;

charfiller[58];

}PCXHEAD;

其中值得注意的是以下几个数据:

manufacturer为PCX文件的标识,必须为0x0a;

xmin为最小的x坐标,xmax最大的x坐标,所以图象的宽度为xmax-xmin+1,同样图象的高度为ymax-yin+1;

bytes_per_line为每个编码行所占的字节数,下面将详细介绍。

PCX的调色板在文件的最后。

以256色PCX文件为例,倒数第769个字节为颜色数的标识,256时该字节必须为12,剩下的768(256×

3)为调色板的RGB值。

为了叙述方便,我们针对256色PCX文件,介绍一下它的解码过程。

编码是解码的逆过程,有兴趣的读者可以试着自己来完成。

解码是以行为单位的,该行所占的字节数由bytes_per_line给定。

为此,我们开一个大小为bytes_per_line的解码缓冲区。

一开始,将缓冲区的所有内容清零。

从文件中读出一个字节C,若C>

0xc0,说明是行程(RunLength)信息,即C的低6位表示后面连续的字节个数(所以最多63个连续颜色相同的象素,若还有颜色相同的象素,将在下一个行程处理),文件的下一个字节就是实际的图象数据(即该颜色在调色板中的索引值)。

若C<

0xc0,则表示C是实际的图象数据。

如此反复,直到这bytes_per_line个字节处理完,这一行的解码完成。

PCX就是有若干个这样的解码行组成。

下面是实现256色PCX文件解码的源程序,其中第二个函数对一行进行解码,应该把阅读的重点放在这个函数上。

要注意的是,执行时文件C:

\\test.pcx必须存在,而且是一个256色PCX文件。

unsignedint 

PcxBytesPerLine;

BOOLLoadPcxFile(HWNDhWnd,char*PcxFileName)

{

FILE 

*PCXfp;

PCXHEAD 

header;

LOGPALETTE 

*pPal;

HPALETTE 

hPrevPalette;

HDC 

hDc;

HLOCAL 

hPal;

DWORD 

ImgSize;

DWORD 

OffBits,BufSize;

LPBITMAPINFOHEADER 

lpImgData;

i;

LONG 

x,y;

int 

PcxTag;

unsignedchar 

LineBuffer[6400];

LPSTR 

lpPtr;

HFILE 

hfbmp;

if((PCXfp=fopen(PcxFileName,"

rb"

))==NULL){//文件没有找到

MessageBox(hWnd,"

Filec:

\\test.pcxnotfound!

"

"

ErrorMessage"

MB_OK|MB_ICONEXCLAMATION);

returnFALSE;

}

//读出头信息

fread((char*)&

header,1,sizeof(PCXHEAD),PCXfp);

if(header.manufacturer!

=0x0a){//不是一个合法的PCX文件

NotavalidPcxfile!

fclose(PCXfp);

returnFALSE;

//将文件指针指向调色板开始处

fseek(PCXfp,-769L,SEEK_END);

//获取颜色数信息

PcxTag=fgetc(PCXfp)&

0xff;

if(PcxTag!

=12){//非256色,返回

MessageBox(hWnd,"

Nota256colorsPcxfile!

//创建新的BITMAPFILEHEADER和BITMAPINFOHEADER

memset((char*)&

bf,0,sizeof(BITMAPFILEHEADER));

bi,0,sizeof(BITMAPINFOHEADER));

//填写BITMAPINFOHEADER头信息

bi.biSize=sizeof(BITMAPINFOHEADER);

//得到图象的宽和高

bi.biWidth=header.xmax-header.xmin+1;

bi.biHeight=header.ymax-header.ymin+1;

bi.biPlanes=1;

bi.biBitCount=8;

bi.biCompression=BI_RGB;

ImgWidth=bi.biWidth;

ImgHeight=bi.biHeight;

NumColors=256;

LineBytes=(DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount);

ImgSize=(DWORD)LineBytes*bi.biHeight;

//填写BITMAPFILEHEADER头信息

bf.bfType=0x4d42;

bf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+

NumColors*sizeof(RGBQUAD)+ImgSize;

bf.bfOffBits=(DWORD)(NumColors*sizeof(RGBQUAD)+

sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER));

//为新图分配缓冲区

if((hImgData=GlobalAlloc(GHND,(DWORD)

(sizeof(BITMAPINFOHEADER)+

NumColors*sizeof(RGBQUAD)+ImgSize)))==NULL)

{

Errorallocmemory!

ErrorMessage"

fclose(PCXfp);

lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);

//拷贝头信息

memcpy(lpImgData,(char*)&

bi,sizeof(BITMAPINFOHEADER));

lpPtr=(char*)lpImgData+sizeof(BITMAPINFOHEADER);

//为256色调色板分配内存

hPal=LocalAlloc(LHND,sizeof(LOGPALETTE)+

NumColors*sizeof(PALETTEENTRY));

pPal=(LOGPALETTE*)LocalLock(hPal);

pPal->

palNumEntries=256;

palVersion=0x300;

for(i=0;

i<

256;

i++){

//读取调色板中的RGB值

palPalEntry[i].peRed=(BYTE)fgetc(PCXfp);

palPalEntry[i].peGreen=(BYTE)fgetc(PCXfp);

palPalEntry[i].peBlue=(BYTE)fgetc(PCXfp);

palPalEntry[i].peFlags=(BYTE)0;

*(lpPtr++)=(unsignedchar)pPal->

palPalEntry[i].peBlue;

palPalEntry[i].peGreen;

palPalEntry[i].peRed;

*(lpPtr++)=0;

//产生新的逻辑调色板

hPalette=CreatePalette(pPal);

LocalUnlock(hPal);

LocalFree(hPal);

hDc=GetDC(hWnd);

if(hPalette){

hPrevPalette=SelectPalette(hDc,hPalette,FALSE);

RealizePalette(hDc);

//解码行所占的字节数

PcxBytesPerLine=(unsignedint)header.bytes_per_line;

//将文件指针指向图象数据的开始处

fseek(PCXfp,(LONG)sizeof(PCXHEAD),SEEK_SET);

//缓冲区大小

OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);

//BufSize为缓冲区大小

BufSize=OffBits+bi.biHeight*LineBytes;

for(y=0;

y<

bi.biHeight;

y++){

//指向新图中相应的位置

lpPtr=(char*)lpImgData+BufSize-LineBytes-y*LineBytes;

//解码该行,放在数组LineBuffer中

ReadPcxLine(LineBuffer,PCXfp);

for(x=0;

x<

bi.biWidth;

x++)

*(lpPtr++)=LineBuffer[x];

//将该行存储到位图数据中

//创建新的位图

hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpImgData,

(LONG)CBM_INIT,

(LPSTR)lpImgData+

sizeof(BITMAPINFOHEADER)+

NumColors*sizeof(RGBQUAD),

(LPBITMAPINFO)lpImgData,

DIB_RGB_COLORS);

if(hPalette&

&

hPrevPalette){

SelectPalette(hDc,hPrevPalette,FALSE);

hfbmp=_lcreat("

c:

\\pcx2bmp.bmp"

0);

_lwrite(hfbmp,(LPSTR)&

bf,sizeof(BITMAPFILEHEADER));

_lwrite(hfbmp,(LPSTR)lpImgData,BufSize);

_lclose(hfbmp);

//释放内

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 历史学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1