标 题:位图八叉树算法
时 间: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颜色匹配到的调色板索引。