八叉树

标 题:位图八叉树算法

时 间:2008/11/08

源 码:BmpConverter.rar

 

1.位图格式
  位图文件主要分为4个部分组成:文件头,信息头,调色板,图像数据。

  

1.) 文件头
   结构定义如下:
   typedef struct tagBITMAPFILEHEADER
   {
       WORD   bfType ;         // 文件类型,必须为 BM .
       DWORD  bfSize ;         // 整个文件的字节大小
       WORD   bfReserved1 ;    // 保留,为0
       WORD   bfReserved2 ;    // 保留,为0
       DWORD  bfOffBits ;      // 图像数据在文件中相对头结构的偏移字节数.
   }BITMAPFILEHEADER ;
2.) 信息头 ;
   结构定义如下:
   typedef struct tagBITMAPINFOHEADER
   {
       DWORD biSize;            // 结构大小
       LONG biWidth;            // 图像宽度(像素)
       LONG biHeight;           // 图像高度(像素)
       WORD biPlanes;           // 图象位平面数目,必须为1
       WORD biBitCount         // 每像素的位数(1,4,8,24)
       DWORD biCompression;    // 压缩类型
       DWORD biSizeImage;      // 压缩图象大小的字节值
       LONG biXPelsPerMeter;    // 水平分辨率
       LONG biYPelsPerMeter;    // 垂直分辨率
       DWORD biClrUsed;        // 使用的颜色数目
       DWORD biClrImportant;    // 重要颜色数目
   } BITMAPINFOHEADER;
3.) 调色板
   每个调色板元素的结构体定义如下:
   typedef struct tagRGBQUAD
   {
       BYTE rgbBlue;
       BYTE rgbGreen;
       BYTE rgbRed;
       BYTE rgbReserved;
   } RGBQUAD;
   对于高位位图,将没有调色板存在.
4.) 图像数据
   包含实际的图像数据,N位位图,每N Bits表示一个像素的值,按从下到上行优先存储,同时每行按4字节对齐.
5.) 读取位图文件
   打开文件 :
       FILE * fBmp = _tfopen(fileName,_T("rb"));
   读取文件头:
       BITMAPFILEHEADER FileHeader;
       fread(&FileHeader,sizeof(BITMAPFILEHEADER),1,fBmp);
   读取信息头:
       BITMAPINFOHEADER InfoHeader;
       fread(&InfoHeader,sizeof(BITMAPINFOHEADER),1,fBmp);
   读取调色板(以256色为例,调色板有256项):
       RGBQUAD quard[256] ;
       fread(quard,sizeof(RGBQUARD),256,fBmp);
   读取图像数据:
       int nDataLen = (((InfoHeader.biWidth*InfoHeader.biBitCount/8) + 3) / 4) * 4 * InfoHeader.biHeight ;
       BYTE * pbyDataBuf = new BYTE[ nDataLen ] ;
       fseek(fBmp,FileHeader.bfOffBits,SEEK_SET);
       fread(pbyDataBuf ,nDataLen ,1,fBmp);

2.八叉树算法
   使用八叉树算法实现真彩色(24Bits)转256色.要实现此功能,主要就是从真彩色中查找出最能代表整张图像的256种颜色,建立调色板.八叉树就是为了找出这256种颜色.
1.) 建立八叉树
   八叉树节点的特性就是每个节点最多有8个字节点,编号为0~7 .
   

   以RGB值建立八叉树,首先建立根节点(Root),然后分别以RGB的每一位分别组成一个0~7的值,依次插入树中。以RGB(123,54,78)为例,
      

  以此类推,将所有的RGB值逐层插入到八叉树中,在每个节点上,记录所有经过的节点的RGB值的总和,已及RGB颜色个数。八叉树节点结构如下:
   typedef struct tagNode
   {
       DWORD  dwCounter ;      // 经过该节点的个数
       DWORD  dwRedSum ;     //  R分量的总和
       DWORD  dwGreenSum ;    // G 分量的总和
       DWORD  dwBlueSum ;     // B 分量的总和
       BOOL    bLeafNode ;      // 是否为叶子节点.
       tagNode *  psChild[8];      // 分别指向该节点编号为0~7的8个子节点.
   }SNode ;
   插入的过程中,如果节点不存在,则需要创建新的节点,然后增加节点计数以及RGB各分量的总和.当在插入时,发现节点已经存在,且是叶子节点,则停止该颜色后续层数节点的插入。插入完一个颜色之后,如果叶子节点数超过了我们要得到的颜色数(256色需要得到256种颜色),这时候就需要合并一些呀字节点了,使的叶子节点的个数不超过我们要得到的颜色数。
   由于越底层的节点,数据的敏感度越低,所以,我们将从最底层的节点开始合并。按节点计数值小的优先合并策略,将其字节点的所有RGB分量以及节点计数全部记录到该节点中,并删除其所有子节点。依此进行,直到合并后的叶子数符合要求为止。
2.) 提取调色板
   按照上述的步骤插入完所有的颜色之后,便建立起一颗叶子节点不超过256的八叉树。此时,取出叶子节点中的RGB分量的平均值(分量总和 / 节点计数),即是得到的调色板颜色值。
3.) 匹配调色板索引
   所谓匹配调色板索引,就是根据原始的RGB值,在调色板中查找出最接近的颜色的索引。对每个RGB颜色,分别对调色板数据求各分量的差值的平方和,求的的最小值对应的调色板颜色的索引,即是该RGB颜色匹配到的调色板索引。

你可能感兴趣的:(八叉树)